• Linux C 多线程


    为什么会有线程?  

          ————————>>>>        进程实现多任务的缺点

    1. 进程间切换的计算机资源开销很大,切换效率非常低
    2. 进程间数据共享的开销也很大

    线程进程关系

    • 线程是进程的一个执行单元,是进程内的调度实体。比进程更小的独立运行的基本单位。线程也被称为轻量级进程
    • 同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间。
    • 进程退出,进程中所有线程全部退出;
    • 一个进程崩溃后,不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。
    • 线程不可能完全替代进程;
    • 线程拥有独立的属性;

    线程是任务调度和执行的基本单位
     

    线程的特点

    线程不安全,不稳定,不健壮————(一个线程的释放可能会影响其他线程)

    线程切换的开销很低————(实质就是函数的切换)

    线程通信机制简单(但不安全)————(访问全局变量)

    线程操作

    线程函数(不是OS提供,不是系统调用API,而是线程库libpthread.a/.so,库函数则可以跨平台)

    线程库和函数手册的安装

    sudo apt-get install glibc-doc :安装线程库
    sudo apt-get install manpages-posix-dev:安装线程库的函数手册

     

    线程创建

    #include
    int pthread_create
    (pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);

    执行顺序:先创建的先执行

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. void *read_mouse(void *ptr)
    10. {
    11. int mouse;
    12. while (1)
    13. {
    14. read(*((int *)ptr), &mouse, sizeof(int));
    15. printf("mouse = %d\n", mouse);
    16. }
    17. }
    18. void *read_keyboard(void *ptr)
    19. {
    20. char buffer[1024] = "0";
    21. while (1)
    22. {
    23. read(0, buffer, sizeof(buffer));
    24. printf("%s\n", buffer);
    25. memset(buffer, 0, sizeof(buffer));
    26. }
    27. }
    28. int main(int argc, char **argv)
    29. {
    30. pthread_t id1;
    31. pthread_t id2;
    32. int fd = open("/dev/input/mouse0", O_RDONLY);
    33. if (fd < 0)
    34. {
    35. perror("fd mouse is error");
    36. exit(-1);
    37. }
    38. if (pthread_create(&id1, NULL, read_mouse, (void *)(&fd)) < 0) // 传递给线程的参数
    39. {
    40. perror("pthread_creat 1 error");
    41. exit(-1);
    42. }
    43. if (pthread_create(&id2, NULL, read_keyboard, NULL) < 0)
    44. {
    45. perror("pthread_creat 2 error");
    46. exit(-1);
    47. }
    48. pause(); // 若无pause,则主进程执行完,所有的线程会结束;所以需要
    49. return 0;
    50. }

    封装:

    1. void *read_mouse(void *ptr)
    2. {
    3. int mouse;
    4. while (1)
    5. {
    6. int fd = open("/dev/input/mouse0", O_RDONLY);
    7. if (fd < 0)
    8. {
    9. perror("fd mouse is error");
    10. exit(-1);
    11. }
    12. read(fd, &mouse, sizeof(int));
    13. printf("mouse = %d\n", mouse);
    14. }
    15. }
    16. void *read_keyboard(void *ptr)
    17. {
    18. char buffer[1024] = "0";
    19. while (1)
    20. {
    21. read(0, buffer, sizeof(buffer));
    22. printf("%s\n", buffer);
    23. memset(buffer, 0, sizeof(buffer));
    24. }
    25. }
    26. int main(int argc, char **argv)
    27. {
    28. pthread_t id[2];
    29. void *(*ptr[2])(void *);
    30. ptr[0] = read_keyboard;
    31. ptr[1] = read_mouse;
    32. for (int i = 0; i < 2; ++i)
    33. {
    34. pthread_create(id + i, NULL, *(ptr+i), NULL);
    35. }
    36. pause(); // 若无pause,则主进程执行完,所有的线程会结束;所以需要
    37. return 0;
    38. }

    线程可以访问全局数据区

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. char buffer[1024];
    10. void *write_data(void *)
    11. {
    12. while (1)
    13. {
    14. scanf("%s", buffer);
    15. }
    16. }
    17. void *read_data(void *)
    18. {
    19. while (1)
    20. {
    21. if (strlen(buffer) != 0)
    22. {
    23. printf("%s\n", buffer);
    24. memset(buffer,0,sizeof(buffer));
    25. }
    26. sleep(3);
    27. }
    28. }
    29. int main(int argc, char **argv)
    30. {
    31. pthread_t id[2];
    32. void *(*func[2])(void *);
    33. func[0] = write_data;
    34. func[1] = read_data;
    35. for (int i = 0; i < 2; ++i)
    36. {
    37. pthread_create(id + i, NULL, *(func + i), NULL);
    38. }
    39. pause();
    40. return 0;
    41. }

    线程退出

    1.被动退出:

    主线程关闭子线程:
    int pthread_cancel(pthread_t thread);
    1)功能
    当次线程是死循环时,可以调动这个函数主动取消该线程
    2)返回值
    成功返回0,失败返回非零错误号。
    2〉参数
    thread:要取消线程的ID号

    1. int main(int argc, char **argv)
    2. {
    3. pthread_t id[2];
    4. void *(*func[2])(void *);
    5. func[0] = write_data;
    6. func[1] = read_data;
    7. for (int i = 0; i < 2; ++i)
    8. {
    9. pthread_create(id + i, NULL, *(func + i), NULL);
    10. }
    11. sleep(5);
    12. pthread_cancel(id[1]);//五秒钟后,次线程被主线程取消了,则不再能读取到buffer内数据
    13. pause();
    14. return 0;
    15. }

    2.主动退出

    2.1 void pthread_exit(void *retval);函数用于退出当前线程,并可选择返回一个指定的值(用的多)

    2.2 return ;     也是直接退出(用得少)

    1. void *read_data(void *)
    2. {
    3. while (1)
    4. {
    5. if (strlen(buffer) != 0)
    6. {
    7. if (strcmp(buffer,"exit")==0)//如果读到了exit就退出
    8. {
    9. pthread_exit(NULL); // 读取到exit,就会主动的退出线程
    10. }
    11. printf("%s\n", buffer);
    12. memset(buffer, 0, sizeof(buffer));
    13. }
    14. }
    15. }
    16. void *read_data(void *)
    17. {
    18. while (1)
    19. {
    20. if (strlen(buffer) != 0)
    21. {
    22. if (strcmp(buffer,"exit")==0)//如果读到了exit就退出
    23. {
    24. return NULL; // 读取到exit,就会主动的退出线程
    25. }
    26. printf("%s\n", buffer);
    27. memset(buffer, 0, sizeof(buffer));
    28. }
    29. }
    30. }

    3.注册线程退出处理函数

    pthread_cleanup_push

    pthread_cleanup_pop

    必须成对出现,且不能在{ }的语句中间,(因为他们是宏定义,各包含一个括号

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. char buffer[1024];
    10. void *read_exit_handler(void *arg)
    11. {
    12. printf("%s\n", (char *)arg);
    13. }
    14. void *write_data(void *)
    15. {
    16. while (1)
    17. {
    18. scanf("%s", buffer);
    19. }
    20. }
    21. void *read_data(void *)
    22. {
    23. pthread_cleanup_push(read_exit_handler, (void *)"cleannup done ");
    24. while (1)
    25. {
    26. if (strlen(buffer) != 0)
    27. {
    28. if (strcmp(buffer, "exit") == 0) // 如果读到了exit就退出
    29. {
    30. pthread_exit(NULL); // 可以调用退出清理函数(出栈)
    31. // return NULL; // 不可以调用退出清理函数(出栈)
    32. }
    33. printf("%s\n", buffer);
    34. memset(buffer, 0, sizeof(buffer));
    35. }
    36. sleep(1); // 无限的循环,会导致无法调用pthread_cancel(id[1]);来调用退出处理函数!!!!!所以给每次循环睡眠一秒
    37. }
    38. pthread_cleanup_pop(0); // 非零值表示会执行清理函数,零值表示不执行清理
    39. //------------------------即使pthread_cleanup_pop(0),但如果还是遇到了 pthread_exit(NULL)和pthread_cancel 还是会执行清理函数-------------------------------------
    40. }
    41. int main(int argc, char **argv)
    42. {
    43. pthread_t id[2];
    44. void *(*func[2])(void *);
    45. func[0] = write_data;
    46. func[1] = read_data;
    47. for (int i = 0; i < 2; ++i)
    48. {
    49. pthread_create(id + i, NULL, *(func + i), NULL);
    50. }
    51. sleep(3);
    52. pthread_cancel(id[1]); // 五秒钟后,次线程被主线程取消了,则不再能读取到buffer内数据
    53. pause();
    54. return 0;
    55. }

    多个线程的退出清理函数执行顺序:

    1. char buffer[1024];
    2. void *read_exit_handler(void *arg)
    3. {
    4. printf("%s\n", (char *)arg);
    5. }
    6. void *read_exit_handler2(void *arg)
    7. {
    8. printf("%s 2\n", (char *)arg);
    9. }
    10. void *write_data(void *)
    11. {
    12. while (1)
    13. {
    14. scanf("%s", buffer);
    15. }
    16. }
    17. void *read_data(void *)
    18. {
    19. pthread_cleanup_push(read_exit_handler, (void *)"cleannup done ");
    20. pthread_cleanup_push(read_exit_handler2, (void *)"cleannup done ");
    21. printf("hello world\n");
    22. pthread_cleanup_pop(!0);
    23. pthread_cleanup_pop(!0); // 非零值表示会执行清理函数,零值表示不执行清理
    24. }
    25. int main(int argc, char **argv)
    26. {
    27. pthread_t id[2];
    28. void *(*func[2])(void *);
    29. func[0] = write_data;
    30. func[1] = read_data;
    31. for (int i = 0; i < 2; ++i)
    32. {
    33. pthread_create(id + i, NULL, *(func + i), NULL);
    34. }
    35. pause();
    36. return 0;
    37. }

    线程等待

    1.等待的目的:

    1.1保证线程的退出顺序:保证一个线程退出并且回收资源后允许下一个线程退出
    1.2回收线程退出时的资源情况:保证当前线程退出后,创建的新线程不会复用刚才退出线程的地址空间
    1.3
    获得新线程退出时的结果是否正确的退出返回值,这个有点类似回收僵尸进程的wait,保证不会发生内存泄露等问题
     

    2.pthread_join(id[1], &ret); // 会阻塞,直到线程结束,才会执行下面的代码;)

    线程状态

            1.线程资源为什么不采用进程退出后一起回收?

            2.Linux下把线程分为可结合态和分离态

    可结合态:(默认的状态) 这种状态下的线程是能被其它进程回收其资源或杀死,资源只能通过pthread_join来回收(不能自己释放,进程主动回收)

    分离态:这种状态下的线程是不能够被其它进程回收其资源或杀死;存储资源在它终止时由系统自动释放

            3.如何避免多线程退出导致的内存泄漏

    3.1 可结合线程都显示调用pthread_join回收

    3.2 将其变成分离态的线程:

    【      

    3.2.1:int pthread_detach(pthread_t thread);        (不阻塞)
    功能:        如果次线程的资源不希望别人调用pthread_join函数来回收的话,而是希望自己在结束时自动回收资源的话,就可以调用这个函教
    这个函数的功能就是分离次线程,让次线程在结束时自动回收资源
    返回值:        成功返回0,失败返回错误号
    参数:        thread:你要分离的那个次线程的TID

    1. int main(int argc, char **argv)
    2. {
    3. pthread_t id[2];
    4. void *(*func[2])(void *);
    5. func[0] = write_data;
    6. func[1] = read_data;
    7. for (int i = 0; i < 2; ++i)
    8. {
    9. if (pthread_create(id + i, NULL, *(func + i), NULL) < 0)
    10. {
    11. perror("pthread creat error");
    12. exit(-1);
    13. }
    14. pthread_detach(id[i]); // 设置为分离态, 资助回收资源,减少内存泄漏
    15. //此时也不需要pthread_join来回收资源了
    16. }
    17. pause();
    18. return 0;
    19. }

    3.2.2 修改属性

    线程实现,向毅个文件里分别写如“hhhhhwwwww”和“helloworld”

    线程同步(难点)

           

             1.进程vs线程

    进程:进程空间天然是独立的,因此进程间资源的保护是天然的(现成的),需要重点关心的进程间的通信
    线程:多线程天然的共享进程空间,因此线程数据共享是天然的(现成的),需要重点关心的是资源的保护

            2.线程的资源保护机制

    2.1互斥锁

    定义一个互斥锁(变量)

    初始化互斥锁:预设互斥锁的初始值

    1.直接赋值

    2.初始化互斥锁的函数:int pthread_mutex_init ( pthread_mutex_t *restrict mutex,const pthread mutexattr_t *restrict attr);
    功能:初始化定义的互斥锁
    什么是初始化:就是设置互斥锁所需要的值。
    返回值:总是返回0,所以这个函数不需要进行出错处理。
    参数:~~~

    【在C语言中,初始化静态全局变量时,只能使用常量表达式。PTHREAD_MUTEX_INITIALIZER`是一个宏,它并不是常量表达式,因此不能直接用于初始化静态全局变量

    如果你想在主函数内初始化互斥锁变量`mutex1`,可以使用`pthread_mutex_init`函数来实现。你可以将以下代码添加到主函数的开头,用于初始化互斥锁:
    pthread_mutex_init(&mutex1, NULL);

    这样,你就可以在主函数中正确地初始化互斥锁,并在其他线程中使用它。

    对于你的代码,你可以这样修改:
    pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;

    或者使用`pthread_mutex_init`函数进行初始化:
    pthread_mutex_t mutex1;
    pthread_mutex_init(&mutex1, NULL);

    这样修改后,`mutex1`将在主函数内正确地进行初始化。】

    加锁、解锁

    pthread_mutex_lock(&mutex1);————pthread_mutex_trylock( &mutex)(非阻塞加锁)

    pthread_mutex_unlock(&mutex1);

    进程退出时销毁互斥锁

    1. void handler(int sig)
    2. {
    3. pthread_mutex_destroy(&mutex1);
    4. }
    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. pthread_mutex_t mutex1= PTHREAD_MUTEX_INITIALIZER;//定义一个互斥锁变量
    11. //*****不能在主函数内调用这句话来初始化静态全局变量
    12. int fd;
    13. void handler(int sig)
    14. {
    15. pthread_mutex_destroy(&mutex1);
    16. }
    17. void *write1(void *arg)
    18. {
    19. while (1)
    20. {
    21. pthread_mutex_lock(&mutex1);
    22. write(fd, "hhhhh", 5);
    23. write(fd, "wwwww", 5);
    24. write(fd, "\n", 1);
    25. pthread_mutex_unlock(&mutex1);
    26. }
    27. }
    28. void *write2(void *arg)
    29. {
    30. while (1)
    31. {
    32. pthread_mutex_lock(&mutex1);
    33. write(fd, "hello", 5);
    34. write(fd, "world", 5);
    35. write(fd, "\n", 1);
    36. pthread_mutex_unlock(&mutex1);
    37. }
    38. }
    39. int main(int argc, char **argv)
    40. {
    41. // pthread_mutex_init(&mutex1, NULL);
    42. pthread_t id[2];
    43. void *(*pfunc[2])(void *);
    44. pfunc[0] = write1;
    45. pfunc[1] = write2;
    46. fd = open("a.txt", O_RDWR | O_CREAT | O_TRUNC, 0655);
    47. if (fd < 0)
    48. {
    49. perror("fd error");
    50. exit(-1);
    51. }
    52. for (int i = 0; i < 2; ++i)
    53. {
    54. if (pthread_create(&id[i], NULL, pfunc[i], NULL) < 0)
    55. {
    56. perror("pthread error");
    57. exit(-1);
    58. }
    59. pthread_detach(id[i]);
    60. }
    61. pause();
    62. signal(SIGINT, handler);
    63. return 0;
    64. }
    65. /-------------------------- -----------------------------/
    66. void *write1(void *arg)
    67. {
    68. while (1)
    69. {
    70. if (pthread_mutex_trylock(&mutex1) == 0)//非阻塞的上锁,如果已经上锁,则返回0;
    71. {
    72. write(fd, "hhhhh", 5);
    73. write(fd, "wwwww", 5);
    74. write(fd, "\n", 1);
    75. pthread_mutex_unlock(&mutex1);
    76. }
    77. else
    78. {
    79. printf("trylock is busy\n");
    80. }
    81. }
    82. }

    注意事项:

    1.凡是访问进程的全局变量,都要加锁;

    2.涉及到共享资源的访问,必须上锁

    【对于一个功能实现中,并不涉及访问共享资源的代码段,尽量不要放在上锁和解锁中间】

    2.2线程信号量

    2.2.1.定义信号量集合


    2.2.2.初始化集合中的每个信号量


    2.2.3.p、v操作

    2.2.4.进程结束时,删除线程信号量集


     

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. sem_t sem1; // 1.定义信号量 (数组)
    12. int fd;
    13. pthread_t id[2];
    14. void handler(int sig) // 4.注册的信号处理函数:用以删除信号量
    15. {
    16. sem_destroy(&sem1);
    17. }
    18. void *write1(void *arg)
    19. {
    20. while (1)
    21. {
    22. sem_wait(&sem1); // 3.p操作
    23. write(fd, "hhhhh", 5);
    24. write(fd, "wwwww", 5);
    25. write(fd, "\n", 1);
    26. sem_post(&sem1); // 3.v操作
    27. }
    28. }
    29. void *write2(void *arg)
    30. {
    31. while (1)
    32. {
    33. sem_wait(&sem1);
    34. write(fd, "hello", 5);
    35. write(fd, "world", 5);
    36. write(fd, "\n", 1);
    37. sem_post(&sem1);
    38. }
    39. }
    40. int main(int argc, char **argv)
    41. {
    42. sem_init(&sem1, 0, 1); // 2.初始化信号量
    43. void *(*pfunc[2])(void *);
    44. pfunc[0] = write1;
    45. pfunc[1] = write2;
    46. fd = open("c.txt", O_RDWR | O_CREAT | O_TRUNC, 0655);
    47. if (fd < 0)
    48. {
    49. perror("fd error");
    50. exit(-1);
    51. }
    52. for (int i = 0; i < 2; ++i)
    53. {
    54. if (pthread_create(&id[i], NULL, pfunc[i], NULL) < 0)
    55. {
    56. perror("pthread error");
    57. exit(-1);
    58. }
    59. pthread_detach(id[i]);
    60. }
    61. pause();
    62. signal(SIGINT, handler);
    63. return 0;
    64. }

    实现google面试题

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. int fd[4]; // 四个文件描述符
    12. sem_t sem_abcd[4]; // 含有四个信号量的数组
    13. pthread_t id[4]; // 四个线程id的数组
    14. void *write1(void *arg)
    15. {
    16. int flag = 0;
    17. while (1)
    18. {
    19. sem_wait(&sem_abcd[0]);
    20. for (int i = 0; i < 4; ++i)
    21. {
    22. if (flag != 0 || i <= 0)
    23. {
    24. write(fd[i], "A", 1);
    25. }
    26. }
    27. flag = 1;
    28. sleep(1);
    29. sem_post(&sem_abcd[1]);
    30. }
    31. }
    32. void *write2(void *arg)
    33. {
    34. int flag = 0;
    35. while (1)
    36. {
    37. sem_wait(&sem_abcd[1]);
    38. for (int i = 0; i < 4; ++i)
    39. {
    40. if (flag != 0 || i <= 1)
    41. {
    42. write(fd[i], "B", 1);
    43. }
    44. }
    45. flag = 1;
    46. sleep(1);
    47. sem_post(&sem_abcd[2]);
    48. }
    49. }
    50. void *write3(void *arg)
    51. {
    52. int flag = 0;
    53. while (1)
    54. {
    55. sem_wait(&sem_abcd[2]);
    56. for (int i = 0; i < 4; ++i)
    57. {
    58. if (flag != 0 || i <= 2)
    59. {
    60. write(fd[i], "C", 1);
    61. }
    62. }
    63. flag = 1;
    64. sleep(1);
    65. sem_post(&sem_abcd[3]);
    66. }
    67. }
    68. void *write4(void *arg)
    69. {
    70. int flag = 0;
    71. while (1)
    72. {
    73. sem_wait(&sem_abcd[3]);
    74. for (int i = 0; i < 4; ++i)
    75. {
    76. if (flag != 0 || i <= 3)
    77. {
    78. write(fd[i], "D", 1);
    79. }
    80. }
    81. flag = 1;
    82. sleep(1);
    83. sem_post(&sem_abcd[0]);
    84. }
    85. }
    86. int main(int argc, char **argv)
    87. {
    88. char temp[1] = "A";
    89. for (int i = 0; i < 4; ++i)
    90. {
    91. temp[0] = temp[0] + i;
    92. if ((fd[i] = open(temp, O_CREAT | O_RDWR | O_TRUNC, 0655)) < 0) // 打开四个文件
    93. {
    94. perror("open error");
    95. exit(-1);
    96. }
    97. temp[0] = 'A';
    98. }
    99. void *(*pfunc[4])(void *) = {
    100. // 函数指针数组
    101. write1,
    102. write2,
    103. write3,
    104. write4,
    105. };
    106. for (int i = 0; i < 4; ++i)
    107. {
    108. if (i == 0)
    109. {
    110. sem_init(&sem_abcd[i], 0, 1); // 先初始化第一个线程的信号量————置为1
    111. }
    112. else
    113. {
    114. sem_init(&sem_abcd[i], 0, 0); // 其他线程的信号量全关闭————置为0
    115. }
    116. if (pthread_create(&id[i], NULL, pfunc[i], NULL) < 0) // 线程创建
    117. {
    118. perror("pthread creat error");
    119. exit(-1);
    120. }
    121. pthread_detach(id[i]); // 分离态
    122. }
    123. pause();
    124. return 0;
    125. }

    功能封装:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. int fd[4]; // 四个文件描述符
    12. sem_t sem_abcd[4]; // 含有四个信号量的数组
    13. pthread_t id[4]; // 四个线程id的数组
    14. int flag = 0;
    15. void *write_demo(void *arg)
    16. {
    17. char c[1] = "A";
    18. int num = *(int *)arg;
    19. c[0] = 'A' + num;
    20. while (1)
    21. {
    22. sem_wait(&sem_abcd[num]);
    23. for (int i = 0; i < 4; ++i)
    24. {
    25. if (flag >= 4 || i <= num)
    26. {
    27. write(fd[i], c, 1);
    28. }
    29. }
    30. flag++;
    31. sleep(1);
    32. sem_post(&sem_abcd[(num + 1) % 4]);
    33. }
    34. }
    35. int main(int argc, char **argv)
    36. {
    37. char temp[1] = "A";
    38. int demo[4] = {0, 1, 2, 3};
    39. for (int i = 0; i < 4; ++i)
    40. {
    41. temp[0] = temp[0] + i;
    42. if ((fd[i] = open(temp, O_CREAT | O_RDWR | O_TRUNC, 0655)) < 0) // 打开四个文件
    43. {
    44. perror("open error");
    45. exit(-1);
    46. }
    47. temp[0] = 'A';
    48. }
    49. for (int i = 0; i < 4; ++i)
    50. {
    51. if (i == 0)
    52. {
    53. sem_init(&sem_abcd[i], 0, 1); // 先初始化第一个线程的信号量————置为1
    54. }
    55. else
    56. {
    57. sem_init(&sem_abcd[i], 0, 0); // 其他线程的信号量全关闭————置为0
    58. }
    59. if (pthread_create(&id[i], NULL, write_demo, (void *)(demo + i)) < 0) // 线程创建
    60. {
    61. perror("pthread creat error");
    62. exit(-1);
    63. }
    64. pthread_detach(id[i]); // 分离态
    65. }
    66. pause();
    67. return 0;
    68. }
    2.3条件变量

    作用:多线程配合工作时,当线程检测到某条件不满足时就休眠,直到别的线程将条件准备好然后通过条件变量将其唤醒

    【需要和互斥锁配合】

    1.定义条件变量:pthread_cond_t;

    2.初始化:

    1.pthread_cond_init(pthread_cond_t *restrict cond,const thread_condattr_t * restrict attr)

    2.赋值初始化:pthread_cond_t cond=PTHREAD_COND_INITLIALIZER

    3.等待条件的函数

    pthread_cond_wait(&cond, &mutex);                 // 不满足条件则阻塞等待,并释放互斥锁

    4.满足条件,通知阻塞的线程

    pthread_cond_signal(&cond);                                 // 满足条件,则通知因cond这一条件变量而阻塞等待的线程,
    或者: pthread_cond_broadcast(&cond);唤醒所有睡眠的进程

    5.销毁条件变量和互斥锁

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. pthread_t id[2];
    10. int val = 0;
    11. pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    12. pthread_cond_t cond; // 1.定义条件变量
    13. // 条件变量的初始化可以 1.在主函数内调用pthread_cond——init初始化; 2.也可以用PTHREAD_COND_INITIALIZER赋值;
    14. void handler_exit(int sig)
    15. {
    16. pthread_mutex_destroy(&mutex);
    17. pthread_cond_destroy(&cond); // 5.注册退出处理函数,删除条件变量
    18. printf(" pthread_cond is cancel\n");
    19. }
    20. void *fun1(void *arg)
    21. {
    22. while (1)
    23. {
    24. pthread_mutex_lock(&mutex);
    25. val++;
    26. if (val == 2)
    27. { // 通知线程2打印
    28. pthread_cond_signal(&cond); // 4.满足条件,则通知因cond这一条件变量而阻塞等待的线程,
    29. // pthread_cond_broadcast(&cond);唤醒所有睡眠的进程
    30. }
    31. pthread_mutex_unlock(&mutex);
    32. sleep(1);
    33. }
    34. }
    35. void *fun2(void *arg)
    36. {
    37. while (1)
    38. {
    39. pthread_mutex_lock(&mutex);
    40. if (val != 2)
    41. {
    42. pthread_cond_wait(&cond, &mutex); // 3.不满足条件则阻塞等待,并释放互斥锁
    43. }
    44. printf("val == 2\n");
    45. val = 0;
    46. pthread_mutex_unlock(&mutex);
    47. }
    48. }
    49. int main(int argc, char **argv)
    50. {
    51. signal(SIGINT, handler_exit);
    52. pthread_cond_init(&cond, NULL); // 2.初始化条件变量
    53. void *(*pfun[2])(void *arg) = {fun1, fun2};
    54. for (int i = 0; i < 2; ++i)
    55. {
    56. if (pthread_create(&id[i], NULL, pfun[i], NULL) < 0)
    57. {
    58. perror("pthread_creat error");
    59. exit(-1);
    60. }
    61. pthread_detach(id[i]);
    62. }
    63. pause();
    64. return 0;
    65. }

  • 相关阅读:
    C#实现基于广度优先BFS求解无权图最短路径----完整程序展示
    365包包模式,月月换新包,如何做到流量经营,把握用户的心?
    mac vscode debug安装调试moodle
    前端开发 开发过程
    悟空crm安装搭建 报错[0] RedisException in Redis.php line 56问题处理办法
    基于微信共享充电桩小程序系统设计与实现 开题报告
    记录一次线上fullgc问题排查过程
    机器学习——boosting之提升树
    信息解码(Message Decoding, ACM/ICPC World Finals 1991, UVa 213)rust解法
    laravel 子查询
  • 原文地址:https://blog.csdn.net/qq_58170320/article/details/132725099