• Linux操作系统-信号量


    信号量也属于一种进程间通信的机制,与其他的进程间通信不同,信号量不是用来传输数据的,而是用来进程间同步与互斥。除此之外,信号量还可以实现线程间的互斥

    信号量是什么?

    信号量的本质是一个计数器。

    一个信号量是由一个内核维护的整数,它的值被限制在大于等于0。对信号量可以进行以下操作:

    1、将信号量设置一个值。

    2、在信号量当前值的基础上加上一个数量(V操作)。

    3、在信号量当前值的基础上减去一个数量(P操作)。

    4、当信号量的值为0时,会被阻塞。

    既然是计数器,那么其中的count也是临界资源,所以PV操作必须得是原子的。

    Linux下信号量的P.V操作如何保证其原子性_weixin_34402090的博客-CSDN博客

    P操作

    相当于count--;

    1. P(){
    2.         lock();
    3.         if(count)
    4.             count--;
    5.         else
    6.             wait(); // 阻塞
    7. unlock();
    8. }

    V操作

    相当于count++

    1. V(){
    2.         lock();
    3.         count++;
    4.         if(count<=0)
    5.             wakeup();//唤醒
    6.         unlock();
    7. }

    注意:V操作的唤醒进程或者线程是不确定的,即具体是哪个进程或者线程被唤醒并允许递减这个信号量是不确定的,仅仅是一个同步机制,而不是一个排队机制。


    POSIX信号量

    头文件 semaphore.h

    1、初始化一个信号量

    sem_init函数

    sem_t 声明一个信号量;

    sem_init函数使用value中指定的值对sem指向的信号量进行初始化。通俗一点就是给信号量中的count值赋一个初值。

    第二个参数要注意

    pshared表明这个信号量是在线程间共享还是在进程间共享。

    如果pshared=0,这个信号量会在调用进程中的线程间进行共享。此时这个信号量应该为临界资源,让线程都可以看到。

    如果pshared=1,这个信号量在进程间共享。此时需要放在内存共享区,即共享内存或者mmap映射区域。

    2、P操作

    sem_wait()函数

    sem_wait函数就是让sem指向的信号量的值减1.

    如果信号量的当前值大于0,则sem_wait立即返回。如果信号量的当前值小于0,那么sem_wait会阻塞到信号量的值大于0为止。

     

    3、V操作

    sem_post函数

    sem_post函数就是将sem指向的信号量的值+1。

    如果在sem_post调用之前信号量的值为0,并且其他某个进程(线程)正在因为等待递减这个信号量而阻塞,那么该进程(线程)会被唤醒。

    4、销毁一个信号量

    sem_destroy函数

     

    sem必须被sem_init初始化才能被销毁

    只有在不存在进程或线程在等待一个信号量时才能够安全销毁这个信号量。


    用信号量实现线程互斥

    用信号量来代替互斥锁mutex。

    思想:将信号量设置为0,1两个状态,这样就可以达到互斥锁的效果。

    1. //一个简单的抢票程序
    2. #include
    3. #include
    4. #include
    5. #include
    6. using namespace std;
    7. class myclass
    8. {
    9. private:
    10. sem_t mtx;
    11. int ticket = 5000;
    12. public:
    13. myclass()
    14. {
    15. sem_init(&mtx, 0, 1);//设置大小为1即和锁一样的特点
    16. }
    17. ~myclass()
    18. {
    19. sem_destroy(&mtx);
    20. }
    21. void P()//封装一下PV操作的函数
    22. {
    23. sem_wait(&mtx);
    24. }
    25. void V()
    26. {
    27. sem_post(&mtx);
    28. }
    29. void Run()
    30. {
    31. pthread_t tid[4];
    32. for (int i = 0; i < 4; i++)//创建四个线程
    33. pthread_create(tid + i, nullptr, Routine, this);
    34. for (int i = 0; i < 4; i++)
    35. pthread_join(tid[i], nullptr);
    36. }
    37. static void* Routine(void* arg)
    38. {
    39. myclass* ptr = (myclass*)arg;
    40. while (1)
    41. {
    42. ptr->P();
    43. if (ptr->ticket > 0)
    44. {
    45. usleep(1000);
    46. cout << "i am thread " << pthread_self() << ", i get tikcet num is " << ptr->ticket << endl;
    47. (ptr->ticket)--;
    48. ptr->V();
    49. }
    50. else
    51. {
    52. ptr->V();
    53. break;
    54. }
    55. }
    56. return nullptr;
    57. }
    58. };
    59. int main()
    60. {
    61. myclass mc;
    62. mc.Run();
    63. return 0;
    64. }

  • 相关阅读:
    AI更改视频语言的神奇网址:让郭德纲讲英语成为现实!
    mysql5.7.44误删除数据后,使用binlog日志恢复
    ERR_PNPM_LINKING_FAILED Error: EPERM: operation not permitted, rename
    【面试经典150 | 哈希表】最长连续序列
    3_docker部署mysql主主备份
    linux 安装R 环境(最新)
    世界星载SAR发展5——SIR-C(1994,美国)
    一步步带你了解一条Sql更新语句是如何执行的
    em/px/rem/vh/vw 的区别?
    LeetCode——1678.设计 Goal 解析器
  • 原文地址:https://blog.csdn.net/weixin_43164548/article/details/126768947