• Linux学习——线程的取消和互斥


    目录

    一、线程的取消:(pthread_cancel)

    运行段错误调试:

    如果没有取消点,手动设置一个

    设置取消使能或禁止

    设置取消类型

    二、线程的清理(pthread (_cleanup_push  _cleanup_pop))

    三、线程通信——互斥

    临界资源概念:

    man手册找不到 pthread_mutex_xxxxxxx (提示No manual entry for pthread_mutex_xxx)的解决方法:

    互斥锁的创建和销毁

    申请锁——pthread_mutex_lock

    释放锁——pthread_mutex_unlock 

    互斥锁的使用:

    四、读写锁

    五、死锁

    概念:

    避免方法:


    一、线程的取消:(pthread_cancel)

    别的大佬的文章:

    linux c学习笔记----线程创建与终止 - 极客先锋 - 博客园

    意义:随时杀掉一个线程

    int pthread_cancel(pthread_t thread);

    注意:线程的取消要有取消点才可以,不是说取消就取消,线程的取消点主要是阻塞的系统调用

    运行段错误调试:

    可以使用gdb调试

    使用gdb 运行代码,gdb  ./youapp

    (gdb) run

    等待出现Thread 1 "pcancel" received signal SIGSEGV, Segmentation fault.

    输入命令bt(打印调用栈)

    (gdb) bt

    #0  0x00007ffff783ecd0 in vfprintf () from /lib/x86_64-linux-gnu/libc.so.6

    #1  0x00007ffff78458a9 in printf () from /lib/x86_64-linux-gnu/libc.so.6

    #2  0x00000000004007f9 in main () at pcancel.c:21

    确定段错误位置是pcancel.c 21行

    如果没有取消点,手动设置一个

    void pthread_testcancel(void);

    设置取消使能或禁止

    int pthread_setcancelstate(int state, int *oldstate);

    PTHREAD_CANCEL_ENABLE

    PTHREAD_CANCEL_DISABLE  //不能取消

    设置取消类型

    int pthread_setcanceltype(int type, int *oldtype);

    PTHREAD_CANCEL_DEFERRED                等到取消点才取消    延时取消

    PTHREAD_CANCEL_ASYNCHRONOUS           目标线程会立即取消     异步取消

    实验程序;

    1. #include
    2. #include
    3. #include
    4. void *func(void *arg){
    5. printf("This is child thread\n");
    6. pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
    7. // while(1)
    8. {
    9. sleep(5);
    10. pthread_testcancel();
    11. }
    12. pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
    13. while(1){
    14. sleep(1);
    15. }
    16. pthread_exit("thread return");
    17. }
    18. int main(){
    19. pthread_t tid;
    20. void *retv;
    21. int i;
    22. pthread_create(&tid,NULL,func,NULL);
    23. sleep(1);
    24. pthread_cancel(tid);
    25. pthread_join(tid,&retv);
    26. // printf("thread ret=%s\n",(char*)retv);
    27. while(1){
    28. sleep(1);
    29. }
    30. }

    二、线程的清理(pthread (_cleanup_push  _cleanup_pop))

    必要性: 当线程非正常终止,需要清理一些资源。

    void pthread_cleanup_push(void (*routine) (void *), void *arg)

    void pthread_cleanup_pop(int execute)

    routine 函数被执行的条件:

    1. 被pthread_cancel取消掉。
    2. 执行pthread_exit
    3. 非0参数执行pthread_cleanup_pop()

    程序:

    1. #include
    2. #include
    3. #include
    4. #if 1
    5. void cleanup(void *arg){
    6. printf("cleanup,arg=%s\n",(char*)arg);
    7. }
    8. void *func(void *arg){
    9. printf("This is child thread\n");
    10. pthread_cleanup_push(cleanup,"abcd");
    11. pthread_exit("thread return");
    12. while(1){
    13. sleep(1);
    14. }
    15. pthread_cleanup_pop(0);
    16. }
    17. int main(){
    18. pthread_t tid;
    19. void *retv;
    20. int i;
    21. pthread_create(&tid,NULL,func,NULL);
    22. sleep(1);
    23. pthread_cancel(tid);
    24. pthread_join(tid,&retv);
    25. //printf("thread ret=%s\n",(char*)retv);
    26. while(1){
    27. sleep(1);
    28. }
    29. }
    30. #endif

    结果:

     当程序改为:

    1. #include
    2. #include
    3. #include
    4. #if 1
    5. void cleanup(void *arg){
    6. printf("cleanup,arg=%s\n",(char*)arg);
    7. }
    8. void *func(void *arg){
    9. printf("This is child thread\n");
    10. pthread_cleanup_push(cleanup,"abcd");
    11. pthread_exit("thread return");
    12. //while(1){
    13. sleep(1);
    14. //}
    15. pthread_cleanup_pop(0);
    16. }
    17. int main(){
    18. pthread_t tid;
    19. void *retv;
    20. int i;
    21. pthread_create(&tid,NULL,func,NULL);
    22. sleep(1);
    23. //pthread_cancel(tid);
    24. pthread_join(tid,&retv);
    25. //printf("thread ret=%s\n",(char*)retv);
    26. while(1){
    27. sleep(1);
    28. }
    29. }
    30. #endif

    结果仍是:

    程序:

    1. #include
    2. #include
    3. #include
    4. #if 1
    5. void cleanup(void *arg){
    6. printf("cleanup,arg=%s\n",(char*)arg);
    7. }
    8. void *func(void *arg){
    9. printf("This is child thread\n");
    10. pthread_cleanup_push(cleanup,"abcd");
    11. //pthread_exit("thread return");
    12. //while(1){
    13. sleep(1);
    14. //}
    15. pthread_cleanup_pop(0);
    16. pthread_exit("thread return");
    17. }
    18. int main(){
    19. pthread_t tid;
    20. void *retv;
    21. int i;
    22. pthread_create(&tid,NULL,func,NULL);
    23. sleep(1);
    24. //pthread_cancel(tid);
    25. pthread_join(tid,&retv);
    26. //printf("thread ret=%s\n",(char*)retv);
    27. while(1){
    28. sleep(1);
    29. }
    30. }
    31. #endif

     结果:

    并没有执行cleanup,因为参数是0,将参数改成1后运行结果:

     

     在clean_up_pop后加上10秒的延时,cleanup依旧很快的就运行了,证明到pop就执行cleanup

     加上cleanup2看看谁先执行

    1. #include
    2. #include
    3. #include
    4. #if 1
    5. void cleanup(void *arg){
    6. printf("cleanup,arg=%s\n",(char*)arg);
    7. }
    8. void cleanup2(void* arg){
    9. printf("cleanup2,arg=%s\n",(char*)arg);
    10. }
    11. void *func(void *arg){
    12. printf("This is child thread\n");
    13. pthread_cleanup_push(cleanup,"abcd");
    14. pthread_cleanup_push(cleanup2,"efgh");
    15. //pthread_exit("thread return");
    16. //while(1){
    17. sleep(1);
    18. //}
    19. pthread_cleanup_pop(1);
    20. pthread_cleanup_pop(1);
    21. sleep(10);
    22. pthread_exit("thread return");
    23. }
    24. int main(){
    25. pthread_t tid;
    26. void *retv;
    27. int i;
    28. pthread_create(&tid,NULL,func,NULL);
    29. sleep(1);
    30. //pthread_cancel(tid);
    31. pthread_join(tid,&retv);
    32. //printf("thread ret=%s\n",(char*)retv);
    33. while(1){
    34. sleep(1);
    35. }
    36. }
    37. #endif

     运行结果:

    证明这个pop和push是像if---else一样成对出现的是先进后出的不用pop改用exit来结束结果相同

    下面试试让线程自己结束自己

    1. #include
    2. #include
    3. #include
    4. #if 1
    5. void cleanup(void *arg){
    6. printf("cleanup,arg=%s\n",(char*)arg);
    7. }
    8. void cleanup2(void* arg){
    9. printf("cleanup2,arg=%s\n",(char*)arg);
    10. }
    11. void *func(void *arg){
    12. printf("This is child thread\n");
    13. pthread_cleanup_push(cleanup,"abcd");
    14. pthread_cleanup_push(cleanup2,"efgh");
    15. //pthread_exit("thread return");
    16. //while(1){
    17. sleep(1);
    18. //}
    19. pthread_cancel(pthread_self());
    20. printf("Should not print\n");
    21. pthread_exit("thread return");
    22. pthread_cleanup_pop(1);
    23. pthread_cleanup_pop(1);
    24. sleep(10);
    25. pthread_exit("thread return");
    26. }
    27. int main(){
    28. pthread_t tid;
    29. void *retv;
    30. int i;
    31. pthread_create(&tid,NULL,func,NULL);
    32. sleep(1);
    33. //pthread_cancel(tid);
    34. pthread_join(tid,&retv);
    35. //printf("thread ret=%s\n",(char*)retv);
    36. while(1){
    37. sleep(1);
    38. }
    39. }
    40. #endif

     结果:

    一个不应该出现的结果出现了,这里不应该打印证明他没有结束,原来是没有设置cancel的取消点,现在加上取消点,加个死循环阻塞系统:

    1. #include
    2. #include
    3. #include
    4. #if 1
    5. void cleanup(void *arg){
    6. printf("cleanup,arg=%s\n",(char*)arg);
    7. }
    8. void cleanup2(void* arg){
    9. printf("cleanup2,arg=%s\n",(char*)arg);
    10. }
    11. void *func(void *arg){
    12. printf("This is child thread\n");
    13. pthread_cleanup_push(cleanup,"abcd");
    14. pthread_cleanup_push(cleanup2,"efgh");
    15. //pthread_exit("thread return");
    16. //while(1){
    17. sleep(1);
    18. //}
    19. pthread_cancel(pthread_self());
    20. printf("Should not print\n");
    21. while(1){
    22. printf("sleep\n");
    23. sleep(1);
    24. }
    25. pthread_exit("thread return");
    26. pthread_cleanup_pop(1);
    27. pthread_cleanup_pop(1);
    28. sleep(10);
    29. pthread_exit("thread return");
    30. }
    31. int main(){
    32. pthread_t tid;
    33. void *retv;
    34. int i;
    35. pthread_create(&tid,NULL,func,NULL);
    36. sleep(1);
    37. //pthread_cancel(tid);
    38. pthread_join(tid,&retv);
    39. //printf("thread ret=%s\n",(char*)retv);
    40. while(1){
    41. sleep(1);
    42. }
    43. }
    44. #endif

     结果为:

    他还是不行原来是因为线程的停止不是立即执行的

    现在将参数改为立即执行:

    在前面加上

    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);

     结果为:

    现在就是立即取消状态了,没有打印出那句话,不设置的话默认为到取消点取消。 

    程序:
     

    1. #include
    2. #include
    3. #include
    4. #if 1
    5. void cleanup(void *arg){
    6. printf("cleanup,arg=%s\n",(char*)arg);
    7. }
    8. void cleanup2(void* arg){
    9. printf("cleanup2,arg=%s\n",(char*)arg);
    10. }
    11. void *func(void *arg){
    12. printf("This is child thread\n");
    13. pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
    14. pthread_cleanup_push(cleanup,"abcd");
    15. pthread_cleanup_push(cleanup2,"efgh");
    16. //pthread_exit("thread return");
    17. //while(1){
    18. sleep(1);
    19. //}
    20. return "1234";
    21. pthread_cancel(pthread_self());
    22. printf("Should not print\n");
    23. while(1){
    24. printf("sleep\n");
    25. sleep(1);
    26. }
    27. pthread_exit("thread return");
    28. pthread_cleanup_pop(1);
    29. pthread_cleanup_pop(1);
    30. sleep(10);
    31. pthread_exit("thread return");
    32. }
    33. int main(){
    34. pthread_t tid;
    35. void *retv;
    36. int i;
    37. pthread_create(&tid,NULL,func,NULL);
    38. sleep(1);
    39. //pthread_cancel(tid);
    40. pthread_join(tid,&retv);
    41. //printf("thread ret=%s\n",(char*)retv);
    42. while(1){
    43. sleep(1);
    44. }
    45. }
    46. #endif

    用return来结束线程,只有一个主线程,成功结束子线程:

    注意:

    1、必须成对使用,即使pthread_cleanup_pop不会被执行到也必须写上,否则编译错误。

    2、pthread_cleanup_pop()被执行且参数为0,pthread_cleanup_push回调函数routine不会被执行.

    3、pthread_cleanup_push 和pthread_cleanup_pop可以写多对,routine执行顺序正好相反

    4、线程内的return 可以结束线程,也可以给pthread_join返回值,但不能触发pthread_cleanup_push里面的回调函数,所以我们结束线程尽量使用pthread_exit退出线程

    三、线程通信——互斥

    临界资源概念:

    不能同时访问的资源,比如写文件,只能由一个线程写,同时写会写乱。

    比如外设打印机,打印的时候只能由一个程序使用。

    外设基本上都是不能共享的资源。

    生活中比如卫生间,同一时间只能由一个人使用。

    必要性: 临界资源不可以共享

    临界区:访问临界资源的代码.。

    man手册找不到 pthread_mutex_xxxxxxx (提示No manual entry for pthread_mutex_xxx)的解决方法:

        apt-get install manpages-posix-dev

    又出现新问题:

    E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)
    E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?
    因为之前下载异常退出导致出现了锁,需要超级用户或者把锁删掉,具体解决办法看这个:

    linux安装软件问题解决(E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission de)_lihua且人机99的博客-CSDN博客

    互斥锁的创建和销毁

    两种方法创建互斥锁,静态方式动态方式

    动态方式:

    int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);

    其中mutexattr用于指定互斥锁属性,如果为NULL则使用缺省属性。

    静态方式:

    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

    锁的销毁:

    int pthread_mutex_destroy(pthread_mutex_t *mutex)

    在Linux中,互斥锁并不占用任何资源,因此LinuxThreads中的 pthread_mutex_destroy()除了检查锁状态以外(锁定状态则返回EBUSY)没有其他动作。

    申请锁——pthread_mutex_lock

    释放锁——pthread_mutex_unlock 

    互斥锁的使用:

    int pthread_mutex_lock(pthread_mutex_t *mutex)

    int pthread_mutex_unlock(pthread_mutex_t *mutex)

    int pthread_mutex_trylock(pthread_mutex_t *mutex)

    vim 设置代码全文格式化:gg=G

    usleep(1)//1毫秒释放CPU

    程序:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #if 1
    6. //pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    7. FILE *fp;
    8. void *func2(void *arg){
    9. pthread_detach(pthread_self());
    10. printf("This is func2 thread\n");
    11. char str[]="You read func2 thread\n";
    12. char c;
    13. int i=0;
    14. while(1){
    15. //pthread_mutex_lock(&mutex);
    16. while(i<strlen(str))
    17. {
    18. c = str[i];
    19. fputc(c,fp);
    20. usleep(1);
    21. i++;
    22. }
    23. //pthread_mutex_unlock(&mutex);
    24. i=0;
    25. usleep(1);
    26. }
    27. pthread_exit("func2 exit");
    28. }
    29. void *func(void *arg){
    30. pthread_detach(pthread_self());
    31. printf("This is func1 thread\n");
    32. char str[]="I read func1 thread\n";
    33. char c;
    34. int i=0;
    35. while(1){
    36. //pthread_mutex_lock(&mutex);
    37. while(i<strlen(str))
    38. {
    39. c = str[i];
    40. fputc(c,fp);
    41. usleep(1);
    42. i++;
    43. }
    44. //pthread_mutex_unlock(&mutex);
    45. i=0;
    46. usleep(1);
    47. }
    48. pthread_exit("func1 exit");
    49. }
    50. int main()
    51. {
    52. pthread_t tid,tid2;
    53. void *retv;
    54. int i;
    55. fp = fopen("1.txt","a+");
    56. if(fp == NULL){
    57. perror("fopen");
    58. return 0;
    59. }
    60. pthread_create(&tid,NULL,func,NULL);
    61. pthread_create(&tid2,NULL,func2,NULL);
    62. while(1){
    63. sleep(1);
    64. }
    65. }
    66. #endif

    结果:

     

     由于没有加锁两个线程同时向1.txt写入,导致出现这种奇怪的东西,为了正常写入加入互斥锁,就是将上面的程序注释掉的地方不注释结果如下:

    四、读写锁

    必要性:提高线程执行效率

    特性:

    写者:写者使用写锁,如果当前没有读者,也没有其他写者,写者立即获得写锁;否则写者将等待,直到没有读者和写者。

    读者:读者使用读锁,如果当前没有写者,读者立即获得读锁;否则读者等待,直到没有写者。

    注意:

    同一时刻只有一个线程可以获得写锁,同一时刻可以有多个线程获得读锁。

    读写锁出于写锁状态时,所有试图对读写锁加锁的线程,不管是读者试图加读锁,还是写者试图加写锁,都会被阻塞。

    读写锁处于读锁状态时,有写者试图加写锁时,之后的其他线程的读锁请求会被阻塞,以避免写者长时间的不写锁

    初始化一个读写锁   pthread_rwlock_init

    读锁定读写锁       pthread_rwlock_rdlock

    非阻塞读锁定     pthread_rwlock_tryrdlock

    写锁定读写锁      pthread_rwlock_wrlock

    非阻塞写锁定      pthread_rwlock_trywrlock

    解锁读写锁         pthread_rwlock_unlock

    释放读写锁         pthread_rwlock_destroy

    当他是读锁时(pthread_rwlock_rdlock):

     当他是写锁时(pthread_rwlock_wrlock)

    加上rewind(fp)在读写之间加一个3秒休眠线程一读完线程二读

    在还没读完的时候强制退出发现并没有进行写操作可以同时读但不能同时写。

    程序:

    1. #include
    2. #include
    3. #include
    4. #include
    5. pthread_rwlock_t rwlock;
    6. FILE *fp;
    7. void * read_func(void *arg){
    8. pthread_detach(pthread_self());
    9. printf("read thread\n");
    10. char buf[32]={0};
    11. while(1){
    12. //rewind(fp);
    13. pthread_rwlock_rdlock(&rwlock);
    14. while(fgets(buf,32,fp)!=NULL){
    15. printf("%d,rd=%s\n",(int)arg,buf);
    16. usleep(1000);
    17. }
    18. pthread_rwlock_unlock(&rwlock);
    19. sleep(1);
    20. }
    21. }
    22. void *func2(void *arg){
    23. pthread_detach(pthread_self());
    24. printf("This func2 thread\n");
    25. char str[]="I write func2 line\n";
    26. char c;
    27. int i=0;
    28. while(1){
    29. pthread_rwlock_wrlock(&rwlock);
    30. while(i<strlen(str))
    31. {
    32. c = str[i];
    33. fputc(c,fp);
    34. usleep(1);
    35. i++;
    36. }
    37. pthread_rwlock_unlock(&rwlock);
    38. i=0;
    39. usleep(1);
    40. }
    41. pthread_exit("func2 exit");
    42. }
    43. void *func(void *arg){
    44. pthread_detach(pthread_self());
    45. printf("This is func1 thread\n");
    46. char str[]="You read func1 thread\n";
    47. char c;
    48. int i=0;
    49. while(1){
    50. pthread_rwlock_wrlock(&rwlock);
    51. while(i<strlen(str))
    52. {
    53. c = str[i];
    54. fputc(c,fp);
    55. i++;
    56. usleep(1);
    57. }
    58. pthread_rwlock_unlock(&rwlock);
    59. i=0;
    60. usleep(1);
    61. }
    62. pthread_exit("func1 exit");
    63. }
    64. int main(){
    65. pthread_t tid1,tid2,tid3,tid4;
    66. void *retv;
    67. int i;
    68. fp = fopen("1.txt","a+");
    69. if(fp==NULL){
    70. perror("fopen");
    71. return 0;
    72. }
    73. pthread_rwlock_init(&rwlock,NULL);
    74. pthread_create(&tid1,NULL,read_func,1);
    75. pthread_create(&tid2,NULL,read_func,2);
    76. //sleep(3);
    77. pthread_create(&tid3,NULL,func,NULL);
    78. pthread_create(&tid4,NULL,func2,NULL);
    79. while(1){
    80. sleep(1);
    81. }
    82. }

    五、死锁

    概念:

    死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

    死锁_百度百科

    避免方法:

    1. 锁越少越好,最好使用一把锁
    2. 调整好锁的顺序

    1. #include
    2. #include
    3. #include
    4. #include
    5. pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    6. pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
    7. FILE *fp;
    8. void *func2(void *arg){
    9. pthread_detach(pthread_self());
    10. printf("This func2 thread\n");
    11. char str[]="I write func2 line\n";
    12. char c;
    13. int i=0;
    14. while(1){
    15. pthread_mutex_lock(&mutex);
    16. printf("%d,I got lock2\n",(int)arg);
    17. sleep(1);
    18. pthread_mutex_lock(&mutex2);
    19. printf("%d,I got 2 locks\n",(int)arg);
    20. pthread_mutex_unlock(&mutex2);
    21. pthread_mutex_unlock(&mutex);
    22. sleep(10);
    23. }
    24. pthread_exit("func2 exit");
    25. }
    26. void *func(void *arg){
    27. pthread_detach(pthread_self());
    28. printf("This is func1 thread\n");
    29. char str[]="You read func1 thread\n";
    30. char c;
    31. int i=0;
    32. while(1){
    33. pthread_mutex_lock(&mutex);
    34. printf("%d,I got lock1\n",(int)arg);
    35. sleep(1);
    36. pthread_mutex_lock(&mutex2);
    37. printf("%d,I got 2 locks\n",(int)arg);
    38. pthread_mutex_unlock(&mutex2);
    39. pthread_mutex_unlock(&mutex);
    40. sleep(10);
    41. }
    42. pthread_exit("func1 exit");
    43. }
    44. int main(){
    45. pthread_t tid,tid2;
    46. void *retv;
    47. int i;
    48. fp = fopen("1.txt","a+");
    49. if(fp==NULL){
    50. perror("fopen");
    51. return 0;
    52. }
    53. pthread_create(&tid,NULL,func,1);
    54. pthread_create(&tid2,NULL,func2,2);
    55. while(1){
    56. sleep(1);
    57. }
    58. }

     

  • 相关阅读:
    git工具 —— git clean 解析
    Pandas 数据排序
    黄金投资面对K线图有哪些好用的交易策略?
    系统编程 day13 (linux) 共享内存的知识 与函数的运用
    从新零售到社区团购,这中间发生了多少变化?
    python运行hhblits二进制命令的包装器类
    java计算机毕业设计springboot+vue园区管理系统
    消息队列Kafka、RocketMQ、RabbitMQ的优劣势比较
    Oracle 中排序碰到 null 值如何处理
    面试必备:聊聊分布式锁的多种实现!
  • 原文地址:https://blog.csdn.net/qq_52479948/article/details/127758365