APUE笔记-线程

线程

线程ID pthread_t

函数:

比较线程ID是否相等:int pthread_equal(pthread_t tid1,pthread_t tid2);

获取自己的线程ID:pthread_t pthread_self(void)

例子:工作队列,主线程控制作业分配,其他线程从作业队列取出自己的作业。需要比较作业中设置的ID和自己的ID。

创建线程:失败返回错误码,不像其他POSIX函数一样设置errno。所以检测返回值,如果不是0,就说明失败。

#include <pthread.h>

int pthread_create(pthread_t *restrict tidp, //返回线程的ID

           const pthread_attr_t *restrict attr, //线程属性

                  void *(*start_rtn)(void), //线程函数入口地址

                  void *restrict arg); //线程函数传参数(如果参数一个以上就用这个)

实验:创建线程

    1 #include <stdio.h>                                                                                                                              
    2 #include "apue.h"
    3 #include <pthread.h>
    4 
    5 void* fun(void *arg)
    6 {
    7     printf("new thread  pid = %lu,tid = %lu\n",(unsigned long)getpid(),(unsigned long)pthread_self());
    8     return NULL;
    9 }
   10 
   11 
   12 int main()
   13 {
   14     pthread_t tid;
   15     if(pthread_create(&tid,NULL,fun,NULL)!=0)
   16     {
   17         printf("creat pthread error\n");
   18         exit(1);
   19     }
   20     sleep(2);
   21     printf("main thread  pid = %lu,tid = %lu\n",(unsigned long)getpid(),(unsigned long)pthread_self());
   22     exit(0);
   23 
   24 }

运行结果:

new thread  pid = 12164,tid = 140259601106688
main thread  pid = 12164,tid = 140259609409280

注意:新线程的ID在tid中,但是新线程不能用这个,因为新线程在pthread_creat返回之前运行,则tid是未初始化的值。Linux2.6对内核的线程库进行了很大的修改。

线程终止

void  pthread_exit(void  *retval)

int pthread_join(pthread_t thread, void **retval);//已经在分离态,就会失败,返回EINVAL。

实验:获取线程的退出码

    1 #include <stdio.h>                                                                                                                              
    2 #include "apue.h"
    3 #include <pthread.h>
    4 
    5 void*fun1(void *p)
    6 {
    7     printf("I am thread 1\n");
    8     return (void*)(1);
    9 }
   10 
   11 
   12 void*fun2(void *p)
   13 {
   14     printf("I am thread 2\n");
   15     pthread_exit((void*)(2));
   16 }
   17 
   18 
   19 int main()
   20 {
   21     pthread_t tid1,tid2;
   22     void *tret;
   23     if(pthread_create(&tid1,NULL,fun1,NULL)!=0)
   24     {
   25         printf("creat thread1 error\n");
   26         exit(1);
   27     }
   28     if(pthread_create(&tid2,NULL,fun2,NULL)!=0)
   29     {
   30         printf("creat thread2 error\n");
   31         exit(2);
   32     }
   33     if(pthread_join(tid1,&tret)!=0)
   34     {
   35         printf("join thread1 error\n");
   36         exit(3);
   37     }
   38     printf("thread1 tret = %d\n",(int)tret);
   39     if(pthread_join(tid2,&tret)!=0)
   40     {
   41         printf("join thread2 error\n");
   42         exit(4);
   43     }
   44     printf("thread1 tret = %d\n",(int)tret);
   45 
   46 }        

运行结果:

I am thread 1
I am thread 2
thread1 tret = 1
thread1 tret = 2

pthread_cancel取消同一进程其他线程, 只是请求。

pthread_cleanup_push

pthread_cleanup_pop

pthread_detach

(1)如果线程只是由于简单的返回而终止的,则清除函数不会被调用。

(2)如果pthread_cleanup_pop被传递0参数,则清除函数不会被调用,但是会清除处于栈顶的清理函数。

实验:

  1 #include <stdio.h>                                                                                                                                
  2 #include <pthread.h>
  3 #include "apue.h"
  4 
  5 
  6 void cleanup(void *arg)
  7 {
  8     printf("clean:%s\n",(char*)arg);
  9 }
 10 
 11 void *fun1(void *arg)
 12 {
 13     printf("pthread1 start\n");
 14     pthread_cleanup_push(cleanup,(void *)"pthread 1 first handler");
 15     pthread_cleanup_push(cleanup,(void *)"pthread 1 second handler");
 16     printf("thread 1 push complete\n");
 17     if(arg)
 18         return (void *)1;
 19     pthread_cleanup_pop(0);
 20     pthread_cleanup_pop(0);
 21     return ((void*)1);
 22 }
 23 void *fun2(void *arg)
 24 {
 25     printf("pthread2 start\n");
 26     pthread_cleanup_push(cleanup,(void *)"pthread 2 first handler");
 27     pthread_cleanup_push(cleanup,(void *)"pthread 2 second handler");
 28     printf("thread 2 push complete\n");
 29     if(arg)//arg为0,就调用pop,则清理函数不会被调用
 30         pthread_exit((void *)2);
 31     pthread_cleanup_pop(0);//参数为0,不会调用清理函数,为非0会调用
 32     pthread_cleanup_pop(0);
 33     pthread_exit((void *)2);
 34 }
 35 
 36 
 37 int main()
 38 {
 39     int err;
 40     pthread_t tid1,tid2;
 41     void *tret;
 42     err = pthread_create(&tid1,NULL,fun1,(void *)1);
 43     if(err!=0)
 44     {
 45         perror("pthread1 creat error");
 46         exit(1);
 47     }
 48     err = pthread_create(&tid2,NULL,fun2,(void *)1);
 49     if(err!=0)
 50     {
 51         perror("pthread2 creat error");
 52         exit(2);
 53     }
 54     err = pthread_join(tid1,&tret);
 55     if(err!=0)
 56     {
 57         perror("pthread1 join error");
 58         exit(3);
 59     }
 60     printf("pthread1 exit code = %ld\n",(long)tret);
 61     err = pthread_join(tid2,&tret);
 62     if(err!=0)
 63     {
 64         perror("pthread2 join error");
 65         exit(4);
 66     }
 67     printf("pthread2 exit code = %ld\n",(long)tret);
 68     exit(0);
 69 }  

运行结果:

pthread1 start
thread 1 push complete
pthread2 start
thread 2 push complete
clean:pthread 2 second handler
clean:pthread 2 first handler
pthread1 exit code = 1
pthread2 exit code = 2

线程同步

一个变量可被多个线程修改,就要同步。

互斥量

互斥量本质是一把锁,释放锁之前,如果很多阻塞着,释放后,谁先抢到,谁就可以对互斥量加锁。

#include <pthread.h> 
//使用默认属性初始化互斥量,则第二个参数为NULL
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); 
  
int pthread_mutex_lock(pthread_mutex_t *mutex); 
  
int pthread_mutex_unlock(pthread_mutex_t *mutex); 

int pthread_mutex_trylock(pthread_mutex_t *mutex); 
  
int pthread_mutex_destroy(pthread_mutex_t *mutex);//动态分配的锁,释放前要调用这个,见实验

实验:使用互斥量保护数据结构

通过增加,减少数据结构的引用计数实现

疑问:APUE的做法,先释放锁,再销毁。(如果释放后,被其他线程锁住,不就会出问题?)

  1 #include <stdio.h>                                                                                                                                
  2 #include <pthread.h>
  3 #include "apue.h"
  4 
  5 typedef struct foo
  6 {
  7     int foo_id;
  8     pthread_mutex_t f_lock;
  9     int foo_count;
 10 }myfoo;
 11 
 12 
 13 myfoo *foo_alloc(int id)
 14 {
 15     myfoo *ret = (myfoo*)malloc(sizeof(myfoo));
 16     ret->foo_count=1;
 17     ret->foo_id = id;
 18     if(pthread_mutex_init(&ret->f_lock,NULL)!=0)
 19     {
 20         free(ret);
 21         return NULL;
 22     }
 23     return ret;
 24 
 25 }
 26 
 27 void foo_hold(myfoo *foo)
 28 {
 29     pthread_mutex_lock(&foo->f_lock);
 30     foo->foo_count++;
 31     pthread_mutex_unlock(&foo->f_lock);
 32 }
 33 
 34 void foo_rele(myfoo *foo)
 35 {
 36     pthread_mutex_lock(&foo->f_lock);
 37     if(--foo->foo_count==0)
 38     {
 39         printf("destroy foo\n");
 40         pthread_mutex_unlock(&foo->f_lock);
 41         pthread_mutex_destroy(&foo->f_lock);
 42         free(foo);
 43     }
 44     else
 45     pthread_mutex_unlock(&foo->f_lock);
 46 }
 47 
 48 int main()
 49 {
 50     myfoo *foo=foo_alloc(1);
 51     foo_hold(foo);
 52     foo_hold(foo);
 53     foo_hold(foo);
 54     printf("%d\n",foo->foo_count);
 55     foo_rele(foo); 
 56     foo_rele(foo); 
 57     foo_rele(foo); 
 58     foo_rele(foo); 
 59     exit(0);
 60 
 61 }        


避免死锁

产生死锁原因:

1、线程试图对一个互斥量加锁两次,自身会陷入死锁。

2、两个线程试图锁住对方正锁住的互斥量。

解决:

1、控制加锁顺序:例如总是先锁住A再锁住B。

2、pthre_mutex_trylock:如果不能获取,先释放占有的锁,过一段时间再试。

323页两个实验

函数pthread_mutex_timelock

读写锁(也叫共享互斥锁)

状态:读模式锁,写模式锁,不加锁

适合对数据结构读次数远大于写次数的情况。

类型:pthread_rwlock_t

pthread_rwlock_init

pthread_rwlock_destroy

pthread_rwlock_rdlock

pthread_rwlock_wrlock

pthread_rwlock_unlock

pthread_rwlock_trywrlock

pthread_rwlock_tryunlock

1 #include <stdio.h>                                                                                                                                
  2 #include <pthread.h>
  3 #include "apue.h"
  4 
  5 typedef struct job
  6 {
  7     struct job *prev;
  8     struct job *next;
  9     pthread_t j_id;
 10 }myjob;
 11 
 12 
 13 typedef struct job_queue
 14 {
 15     myjob *head;
 16     myjob *tail;
 17     pthread_rwlock_t q_lock;
 18 }myjob_queue;
 19 
 20 int queue_init(myjob_queue *queue)
 21 {
 22     int err;
 23     queue->head=NULL;
 24     queue->tail=NULL;
 25     err = pthread_rwlock_init(&queue->q_lock,NULL);
 26     if(err!=0)
 27         return err;
 28     else
 29         return 0;
 30 }
 31 
 32 void queue_insert(myjob_queue *queue,myjob *job)
 33 {
 34     pthread_rwlock_wrlock(&queue->q_lock);
 35     job->next = queue->head;
 36     job->prev=NULL;
 37     if(queue->head!=NULL)
 38     {
 39         queue->head->prev=job;
 40     }
 41     else
 42     {
 43         queue->tail = job;
 44     }
 45     queue->head = job;
 46     pthread_rwlock_unlock(&queue->q_lock);
 47 }
 48 
 49 void queue_append(myjob_queue *queue,myjob *job)
 50 {
 51     pthread_rwlock_wrlock(&queue->q_lock);
 52     job->next = NULL;
 53     job->prev=queue->tail;
 54     if(queue->tail!=NULL)
 55     {
 56         queue->tail->next=job;
 57     }
 58     else
 59     {
 60         queue->head = job;
 61     }
 62     queue->tail = job;
 63     pthread_rwlock_unlock(&queue->q_lock);
 64 }
 65 
 66 void queue_remove(myjob_queue *queue,myjob *job)
 67 {
 68     pthread_rwlock_wrlock(&queue->q_lock);
 69     if(job==queue->head)
 70     {
 71        queue->head=job->next;
 72        if(job==queue->tail)
 73        {
 74            queue->tail=NULL;
 75        }
 76        else
 77        {
 78            job->next->prev=job->prev;
 79        }
 80     }
 81     else if(job==queue->tail)
 82     {
 83         queue->tail=job->prev;
 84         if(job == queue->head)
 85         {
 86             queue->head=NULL;
 87         }
 88         else
 89         {
 90             job->prev->next=job->next;
 91         }
 92     }
 93     else
 94     {
 95         job->next->prev=job->prev;
 96         job->prev->next=job->next;
 97     }
 98     pthread_rwlock_unlock(&queue->q_lock);
 99 }
100 
101 myjob *findjob(myjob_queue *queue,pthread_t id)
102 {
103     myjob *ret;
104     if(pthread_rwlock_rdlock(&queue->q_lock)!=0)
105         return NULL;
106     for(ret = queue->head;ret!=NULL;ret = ret->next)
107     {
108         if(ret->j_id==id)
109         {
110             break;
111         }
112     }
113     pthread_rwlock_unlock(&queue->q_lock);
114     return ret;
115     
116 }   

带有超时的读写锁

条件变量

类型:pthread_cont_t

pthread_cond_init

pthread_cond_destroy

pthread_cond_wait

释放之前锁定的互斥量,然后阻塞,等到被唤醒的时候,锁住互斥量。

pthread_cond_timewait

这些超时的函数用的是绝对时间。因此获取当前时间,并且加上定时的时间就可以。

pthread_cond_signal

pthread_cond_broadcast

实验:生产者消费者模型

    1 #include <stdio.h>                                                                                                                              
    2 #include <time.h>
    3 #include <pthread.h>
    4 #include "apue.h"
    5 
    6 pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
    7 pthread_cond_t producted = PTHREAD_COND_INITIALIZER;
    8 
    9 struct node
   10 {
   11     struct node *next;
   12     int date;
   13 };
   14 
   15 struct node *head;
   16 
   17 void *productor(void *p)
   18 {
   19     while(1)
   20     {
   21         struct node *mp = (struct node *)malloc(sizeof(struct node)); 
   22         pthread_mutex_lock(&lock);
   23         mp->date=rand()%100;
   24         mp->next = head->next;
   25         printf("I product %d\n",mp->date);
   26         head->next = mp;
   27         pthread_mutex_unlock(&lock);
   28         pthread_cond_signal(&producted);
   29         sleep(rand()%5);
   30     }
   31     return NULL;
   32 }
   33 
   34 void *consumer(void *p)
   35 {
   36     struct node *tmp;
   37     while(1)
   38     {
   39         pthread_mutex_lock(&lock);
   40         while(head->next==NULL)
   41         {
   42             pthread_cond_wait(&producted,&lock);
   43         }
   44         tmp = head->next;
   45         head->next = head->next->next;
   46         printf("I consum:%d\n",tmp->date);
   47         pthread_mutex_unlock(&lock);
   48         free(tmp);
   49         sleep(rand()%5);
   50     }
   51     return  NULL;
   52 }
   53 
   54 int main()
   55 {
   56     pthread_t tid1,tid2;
   57     head = (struct node *)malloc(sizeof(struct node));
   58     head->next=NULL;
   59     head->date=0;
   60     srand((unsigned int)time(NULL));
   61     if(pthread_create(&tid1,NULL,productor,NULL)!=0)
   62         perror("creat thread 1 failed");
   63     if(pthread_create(&tid2,NULL,consumer,NULL)!=0)
   64         perror("creat thread 2 failed");
   65     if(pthread_join(tid1,NULL)!=0)
   66         perror("can't join thread 1");
   67     if(pthread_join(tid1,NULL)!=0)
   68         perror("can't join thread 2");
   69     exit(0);
   70 }               

自旋锁

自旋锁是专为防止多处理器并发而引入的一种锁,它在内核中大量应用于中断处理等部分。

屏障

协调多个线程同步。

pthread_join也是一种屏障,一个线程等待,直到另一个线程退出。

类型pthread_barrier_t

pthread_barrier_wait等待,等到返回次数等于设置的初始值,就可以继续,否则阻塞。

 int pthread_barrier_destroy(pthread_barrier_t *barrier);
 int pthread_barrier_init(pthread_barrier_t *restrict barrier,
         const pthread_barrierattr_t *restrict attr, unsigned count);

实验:4个线程分别计数10次,全部计数完毕,主线程退出。

    1 #include <stdio.h>
    2 #include "apue.h"
    3 #include <pthread.h>
    4 
    5 pthread_barrier_t t;
    6 
    7 
    8 void* fun(void *p)
    9 {
   10     for(int i=1;i<=10;i++)
   11     {
   12         printf("I am pthread %d:%d\n",(int)p,i);
   13         sleep(1);
   14     }
   15     pthread_barrier_wait(&t);
   16 }
   17 
   18 int main()
   19 {
   20     pthread_t tid;
   21     pthread_barrier_init(&t,NULL,5);//设置为6,就永远阻塞住了                                                                                                       
   22     for(int i=0;i<4;i++)
   23     {
   24         pthread_create(&tid,NULL,fun,(void*)i);
   25     }
   26     pthread_barrier_wait(&t);
   27     printf("All pthread complete\n");
   28     exit(0);
   29 }

运行结果

理解:5个线程都处于等待状态,就可以继续,否则阻塞。

I am pthread 0:1
I am pthread 1:1
I am pthread 2:1
I am pthread 3:1
I am pthread 3:2
I am pthread 1:2
I am pthread 0:2
I am pthread 2:2
I am pthread 3:3
I am pthread 1:3
I am pthread 0:3
I am pthread 2:3
I am pthread 3:4
I am pthread 1:4
I am pthread 2:4
I am pthread 0:4
I am pthread 3:5
I am pthread 1:5
I am pthread 2:5
I am pthread 0:5
I am pthread 3:6
I am pthread 1:6
I am pthread 2:6
I am pthread 0:6
I am pthread 3:7
I am pthread 1:7
I am pthread 0:7
I am pthread 2:7
I am pthread 3:8
I am pthread 1:8
I am pthread 0:8
I am pthread 2:8
I am pthread 3:9
I am pthread 1:9
I am pthread 0:9
I am pthread 2:9
I am pthread 3:10
I am pthread 0:10
I am pthread 1:10
I am pthread 2:10
All pthread complete

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值