• C语言:详细说明线程的同步操作:互斥,无名信号量,条件变量,以及需要注意的一些问题


    一.线程的互斥

    1.1概念:

    在多线程中,如果多线程同时在访问同一个全局变量,就会出现多个线程在获取变量值时候获取的是同一个值,此时在线程中操作这个变量就会出现不同步的效果。下面这幅图就能够演示出对应的效果。可以使用线程的互斥锁来解决这样的问题。

    1.2代码实现:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #define ERROR(msg) do{\
    6. printf("%s %s %d\n", __FILE__, __func__, __LINE__);\
    7. printf(msg);\
    8. exit(-1); \
    9. }while(0)
    10. pthread_mutex_t lock;
    11. int money = 1000;
    12. void* task1(void* argc)
    13. {
    14. while (1){
    15. sleep(1);
    16. pthread_mutex_lock(&lock);
    17. if (money > 0){
    18. money -= 50;
    19. printf("张三取了50块钱,还剩下%d\n",money);
    20. }else {
    21. printf("张三取钱失败\n");
    22. }
    23. pthread_mutex_unlock(&lock);
    24. }
    25. }
    26. void* task2(void* argc)
    27. {
    28. while (1){
    29. sleep(1);
    30. pthread_mutex_lock(&lock);
    31. if (money > 0){
    32. money -= 100;
    33. printf("李四取了100块钱,还剩下%d\n",money);
    34. }else {
    35. printf("李四取钱失败\n");
    36. }
    37. pthread_mutex_unlock(&lock);
    38. }
    39. }
    40. int main(int argc, char const *argv[])
    41. {
    42. pthread_t pid1, pid2;
    43. pthread_mutex_init(&lock, NULL);
    44. if (pthread_create(&pid1, NULL, task1, NULL)){
    45. ERROR("创建失败");
    46. }
    47. if (pthread_create(&pid2, NULL, task2, NULL)){
    48. ERROR("create error");
    49. }
    50. pthread_join(pid1, NULL);
    51. pthread_join(pid2, NULL);
    52. pthread_mutex_destroy(&lock);
    53. return 0;
    54. }

    二.无名信号量:

    2.1概念:

    线程同步,提前已经知道线程的执行顺序,让线程顺序执行的过程就是同步。

    典型的就是生产者和消费者模型。

    2.2代码实现:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #define ERROR(msg) do{\
    6. printf("%s %s %d\n", __FILE__, __func__, __LINE__);\
    7. printf(msg);\
    8. exit(-1); \
    9. }while(0)
    10. sem_t sem1, sem2;
    11. void* task1(void* argc)
    12. {
    13. while(1){
    14. sem_wait(&sem1);
    15. printf("我生产了一辆汽车\n");
    16. sem_post(&sem2);
    17. }
    18. }
    19. void* task2(void* argc)
    20. {
    21. while(1){
    22. sem_wait(&sem2);
    23. printf("我买了一辆汽车\n");
    24. sem_post(&sem1);
    25. }
    26. }
    27. int main(int argc, char const *argv[])
    28. {
    29. pthread_t pid1, pid2;
    30. sem_init(&sem1, 0, 1);
    31. sem_init(&sem2, 0, 0);
    32. if (pthread_create(&pid1, NULL, task1, NULL)){
    33. ERROR("create error");
    34. }
    35. if (pthread_create(&pid2, NULL, task2, NULL)){
    36. ERROR("create error");
    37. }
    38. pthread_join(pid1, NULL);
    39. pthread_join(pid2, NULL);
    40. sem_destroy(&sem1);
    41. sem_destroy(&sem2);
    42. return 0;
    43. }

    三.条件变量

    3.1条件变量相比于无名信号量的区别:

    无名信号量适合在线程数比较少的线程中实现同步过程,而条件变量适合在大量线程

    实现同步过程。例如条件变量的使用场景如下:比如你要编写一个12306买票的服务器

    当客户端访问服务器的时候,服务器会创建一个线程服务于这个用户。如果有多个用户

    同时想买票,此时服务需要在瞬间创建一堆线程,这个时间比较长,对用户的体验感不好。

    所以12306服务是在启动的时候都已经创建好一堆线程。调用pthread_cond_wait让这些

    线程休眠,当有客户端请求买票的时候,只需要唤醒这些休眠的线程即可,有于省去了

    创建线程的时候,所以这种方式的效率非常的高。

    3.2代码实现:

    1. #include
    2. #include
    3. #include
    4. #define ERROR(msg) do{\
    5. printf("%s %s %d\n", __FILE__, __func__, __LINE__);\
    6. printf(msg);\
    7. exit(-1); \
    8. }while(0)
    9. pthread_cond_t cond;
    10. pthread_mutex_t lock;
    11. int i = 0;
    12. void* task1(void* argc)
    13. {
    14. while (1){
    15. pthread_mutex_lock(&lock);
    16. while(i != 0){
    17. pthread_cond_wait(&cond,&lock);
    18. }
    19. printf("我生产了一辆汽车\n");
    20. i = 1;
    21. pthread_cond_signal(&cond);
    22. pthread_mutex_unlock(&lock);
    23. }
    24. }
    25. void* task2(void* argc)
    26. {
    27. while (1){
    28. pthread_mutex_lock(&lock);
    29. while(i == 0){
    30. pthread_cond_wait(&cond,&lock);
    31. }
    32. printf("我买了一辆汽车\n");
    33. i = 0;
    34. pthread_cond_signal(&cond);
    35. pthread_mutex_unlock(&lock);
    36. }
    37. }
    38. int main(int argc, char const *argv[])
    39. {
    40. pthread_t pid1, pid2;
    41. pthread_mutex_init(&lock, NULL);
    42. pthread_cond_init(&cond, NULL);
    43. if (pthread_create(&pid1, NULL, task1, NULL)){
    44. ERROR("create error");
    45. }
    46. if(pthread_create(&pid2, NULL, task2, NULL)){
    47. ERROR("create error");
    48. }
    49. pthread_join(pid1, NULL);
    50. pthread_join(pid2, NULL);
    51. pthread_cond_destroy(&cond);
    52. pthread_mutex_destroy(&lock);
    53. return 0;
    54. }

    3.3讲解一下为什么这个地方要用while而不用if

    如果用if的话,假如是一号线程先抢到这个锁,然后生产了一辆汽车,然后唤醒了一个线程,因为此时还没有线程,然后释放锁,这个时候如果是二号进程抢到锁,那用if和while都一样,但是如果还是一号线程抢到这个锁,然后进入if i!= 0,然后由于上次唤醒的一个线程还没有用,所以此时立马唤醒,然后又生产了一辆车,这个时候就出现问题了,但是如果用while就不会,因为此时,必须得二号进程,把i == 0,这个才能往下走,不然一号进程一直在那个while循环里面。

  • 相关阅读:
    麒麟操作系统设置QT程序开机自启动有效方法
    媒体文件格式分析之FMP4
    rails的接口查询详解
    C++之单字符串匹配问题
    数据结构:排序
    自动化测试之路 —— Appium元素定位
    机器学习笔记之最优化理论与方法(十)无约束优化问题——共轭梯度法背景介绍
    在香橙派OrangePi 3 LTS开发板上安装向日葵远程控制软件的操作方法
    【LeetCode】哈希表题解汇总
    技术学习:Python(14)|爬虫requests库
  • 原文地址:https://blog.csdn.net/a2998658795/article/details/126323534