线程

概述

image.png
image.png
image.png

创建线程

image.png

  1. #makefile
  2. file: file.c
  3. gcc -g $^ -lpthread -o $@ -w
  4. clean:
  5. rm -rf file*
  1. #main.c
  2. #include <unistd.h>
  3. #include <stdio.h>
  4. #include<pthread.h>
  5. //gcc xxx.c -lpthread -o main
  6. //线程回调函数
  7. void* workthread(void* param){
  8. //子进程循环10000次
  9. for(int i=0;i<10000;i++){
  10. printf("循环第[%d]次 ,child进程id :%x\n",i,pthread_self());
  11. sleep(1);
  12. }
  13. //销毁资源
  14. pthread_detach(pthread_self);//这里也可以不写,因为下面写了pthread_join,或者这里写了下面不写
  15. return NULL;
  16. }
  17. int main(){
  18. pthread_t tid;
  19. pthread_create(
  20. &tid, //线程id
  21. NULL, //线程属性
  22. workthread, //线程回调函数
  23. NULL //传递给线程的参数
  24. );
  25. //父进程循环10000次
  26. for(int i=0;i<10000;i++){
  27. printf("循环第[%d]次 ,parent父进程id :%x\n",i,pthread_self());
  28. sleep(1);
  29. }
  30. //等待线程结束
  31. pthread_join(tid,NULL);
  32. return 0;
  33. }

image.png

线程同步

多个线程同时访问共享数据时可能会冲突。

共享冲突(线程不阻塞)

  1. file: file.c
  2. gcc -g $^ -lpthread -o $@
  3. clean:
  4. rm -rf file*
  1. #include <stdio.h>
  2. #include <pthread.h>
  3. #include <unistd.h>
  4. //全局变量(共享资源)
  5. int g_source = 0;
  6. //线程1
  7. void* workthread1(void * param){
  8. //循环增加10000次
  9. for(size_t i= 0;i< 10000; i++){
  10. g_source += 1;
  11. // 0x00000000004007ac <+22>: mov 0x2008b2(%rip),%eax # 0x601064 <g_source>
  12. // 0x00000000004007b2 <+28>: add $0x1,%eax
  13. // 假设这是eax是1000,这是切换了线程,再切换回来,eax仍然是1000,刚刚的线程白加了
  14. // 0x00000000004007b5 <+31>: mov %eax,0x2008a9(%rip) # 0x601064 <g_source>
  15. }
  16. //销毁线程
  17. pthread_detach(pthread_self());
  18. return NULL;
  19. }
  20. //线程2
  21. void* workthread2(void * param){
  22. //循环增加10000次
  23. for (size_t i= 0;i< 10000;i++){
  24. g_source += 1;
  25. }
  26. //销毁线程
  27. pthread_detach(pthread_self());
  28. return NULL;
  29. }
  30. int main(){
  31. pthread_t threads[2]={};
  32. //创建两个线程
  33. pthread_create(&threads[0],NULL,workthread1,0);
  34. pthread_create(&threads[1],NULL,workthread2,0);
  35. //等待线程销毁
  36. for (size_t i= 0;i< 2;i++){
  37. pthread_join(threads[i],NULL);
  38. }
  39. //显示全局变量的内容
  40. printf("g_source %d \n",g_source);
  41. return 0;
  42. }

image.png
image.png
image.png

互斥锁

线程阻塞

image.png

  1. //定义互斥锁复量
  2. pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
  1. //加锁
  2. pthread_mutex_lock(&g_mutex);
  3. g_source += 1;
  4. pthread_mutex_unlock(&g_mutex);

线程不阻塞

  1. #include <stdio.h>
  2. #include <pthread.h>
  3. #include <unistd.h>
  4. //全局变量(共享资源)
  5. int g_source = 0;
  6. //互斥锁复量
  7. pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
  8. //线程1
  9. void* workthread1(void * param){
  10. //循环增加10000次
  11. for(size_t i= 0;i< 10000; i++){
  12. pthread_mutex_lock(&g_mutex);
  13. g_source += 1;
  14. pthread_mutex_unlock(&g_mutex);
  15. // 0x00000000004007ac <+22>: mov 0x2008b2(%rip),%eax # 0x601064 <g_source>
  16. // 0x00000000004007b2 <+28>: add $0x1,%eax
  17. // 假设这是eax是1000,这是切换了线程,再切换回来,eax仍然是1000,刚刚的线程白加了
  18. // 0x00000000004007b5 <+31>: mov %eax,0x2008a9(%rip) # 0x601064 <g_source>
  19. }
  20. //销毁线程
  21. pthread_detach(pthread_self());
  22. return NULL;
  23. }
  24. //线程2
  25. void* workthread2(void * param){
  26. //循环增加10000次
  27. for (size_t i= 0;i< 10000;i++){
  28. pthread_mutex_lock(&g_mutex);
  29. g_source += 1;
  30. pthread_mutex_unlock(&g_mutex);
  31. }
  32. //销毁线程
  33. pthread_detach(pthread_self());
  34. return NULL;
  35. }
  36. //线程3 不阻塞的线程
  37. void* workthread3(void * param){
  38. //循环增加10000次
  39. for (size_t i= 0;i< 10000;i++){
  40. if(0!= pthread_mutex_trylock(&g_mutex)){
  41. i--;
  42. continue;
  43. }
  44. g_source += 1;
  45. pthread_mutex_unlock(&g_mutex);
  46. }
  47. //销毁线程
  48. pthread_detach(pthread_self());
  49. return NULL;
  50. }
  51. int main(){
  52. pthread_t threads[3]={};
  53. //创建两个线程
  54. pthread_create(&threads[0],NULL,workthread1,0);
  55. pthread_create(&threads[1],NULL,workthread2,0);
  56. pthread_create(&threads[2],NULL,workthread3,0);
  57. //等待线程销毁
  58. for (size_t i= 0;i< 2;i++){
  59. pthread_join(threads[i],NULL);
  60. }
  61. //显示全局变量的内容
  62. printf("g_source %d \n",g_source);
  63. return 0;
  64. }

死锁

image.png
image.png

信号量

互斥锁用于互斥,信号量用于同步
image.png

  1. #include <stdio.h>
  2. #include <pthread.h>
  3. #include <unistd.h>
  4. #include <semaphore.h>
  5. //全局变量(共享资源)
  6. int g_source = 0;
  7. //信号量
  8. sem_t g_semaphore;
  9. //线程1
  10. void* workthread1(void * param){
  11. //循环增加10000次
  12. for(size_t i= 0;i< 10000; i++){
  13. sem_wait(&g_semaphore);//等待信号 ,当等到信号后 ,会将数-1
  14. g_source += 1;
  15. sem_post(&g_semaphore);//释放信号,将信号值+1
  16. }
  17. //销毁线程
  18. pthread_detach(pthread_self());
  19. return NULL;
  20. }
  21. //线程2
  22. void* workthread2(void * param){
  23. //循环增加10000次
  24. for(size_t i= 0;i< 10000; i++){
  25. sem_wait(&g_semaphore);//等待信号 ,当等到信号后 ,会将数-1
  26. g_source += 1;
  27. sem_post(&g_semaphore);//释放信号,将信号值+1
  28. }
  29. //销毁线程
  30. pthread_detach(pthread_self());
  31. return NULL;
  32. }
  33. int main(){
  34. //初始化信号量
  35. sem_init(
  36. &g_semaphore, //信号量
  37. 0, //0表示同一个进程下的线程同步
  38. 1 //信号量的最大值,1的时候可以互斥
  39. );
  40. pthread_t threads[2]={};
  41. //创建两个线程
  42. pthread_create(&threads[0],NULL,workthread1,0);
  43. pthread_create(&threads[1],NULL,workthread2,0);
  44. //等待线程销毁
  45. for (size_t i= 0;i< 2;i++){
  46. pthread_join(threads[i],NULL);
  47. }
  48. //显示全局变量的内容
  49. printf("g_source %d \n",g_source);
  50. //释放信号量
  51. sem_destroy(&g_semaphore);
  52. return 0;
  53. }

信号量常用与生成者与消费者模式