《APUE》读书笔记—第十一章线程

  一个进程在同一时刻只能做一件事情,线程可以把程序设计成在同一时刻能够做多件事情,每个线程处理各自独立的任务。线程包括了表示进程内执行环境必需的信息,包括进程中标识线程的线程ID、一组寄存器值、栈、调度优先级和策略、信号屏蔽字、errno变量以及线程似有数据。进程的所有信息对该进程的所有线程都是共享的,包括可执行的程序文本、程序的全局内存、栈及文件描述符。

  使用线程的好处:(1)为每种事件分配单独的线程、能够简化处理异步事件的代码;(2)多个线程自动地可以访问相同的存储地址空间和文件描述符;(3)将一个问题分解为多个程序,改善整个程序的吞吐量;(4)使用多线程改善交互程序的响应时间。

  进程与线程关系:进程是系统中程序执行和资源分配的基本单位。每个进程有自己的数据段、代码段和堆栈段。线程通常叫做轻型的进程。线程是在共享内存空间中并发执行的多道执行路径,他们共享一个进程的资源。因为线程和进程比起来很小,所以相对来说,线程花费更少的CPU资源。

 

1、线程标识

  进程ID在整个系统中时唯一的,但线程ID只在它所属的进程环境中有效。线程ID用pthread_t数据类型来表示,实现的时候用一个结构来代表pthread_t数据类型。线程ID操作函数如下:

  #include <pthread.h>
  int pthread_equal(pthread_t t1, pthread_t t2);//比较两个线程ID
  pthread_t pthread_self(void);  //获取调用线程ID

2、线程创建

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

thread指向的内存单元被设置为新创建线程的ID,attr用于指定线程属性,start_routine是新线程开始执行的函数地址,arg是函数参数。如果是多个参数,可以将把参数存放在一个结构中,然后将结构地址传递给arg。线程创建时不能保证哪个线程会先运行。写个程序创建一个线程,输出线程标识符,程序如下:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <errno.h>
 5 #include <sys/types.h>
 6 #include <pthread.h>
 7
 8 pthread_t   ntid;
 9 void printpthreadinfo(const char* str);
10 void* thread_func(void *arg);
11
12 int main()
13 {
14     int err;
15     //创建一个新线程
16     err = pthread_create(&ntid,NULL,thread_func,NULL);
17     if(err != 0)
18     {
19         perror("pthread_create()");
20         exit(-1);
21     }
22     printpthreadinfo("main thread: ");
23     sleep(2);  //给新线程时间执行
24     return 0;
25 }
26 void printpthreadinfo(const char* str)
27 {
28     pid_t pid;
29     pthread_t tid;
30     pid = getpid();
31     tid = pthread_self();
32     printf("%s pid %u tid %u (0x%x)\n",str,(unsigned int)pid,(unsigned int)tid,(unsigned int)tid);
33 }
34 void* thread_func(void *arg)
35 {
36     printpthreadinfo("new thread:  ");
37     return ((void*) 0);
38 }

由于pthread库不是Linux系统默认的库,连接时需要使用库libpthread.a,所以在使用pthread_create创建线程时,在编译中要加-lpthread参数。执行结果如下:

3、线程终止

  如果进程中的任一个线程调用了exit、_exit、_Exit函数,那么整个进程就会终止。单个线程终止方式:(1)线程只是从启动例程中返回,返回值是线程的退出码;(2)线程可以被同一进程的其他线程取消;(3)线程调用pthread_exit函数。线程终止函数原型如下:

void pthread_exit(void *retval);   //retval终止状体
int pthread_join(pthread_t thread, void **retval); //获取线程终止状态

其他线程通过调用pthread_join函数获取线程终止状态,调用线程将一直阻塞,直到指定的线程调用pthread_exit、从启动例程中返回或者被取消。写个程序创建两个线程,获取线程退出状态。程序如下:

 1 #include <pthread.h>
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <unistd.h>
 5 #include <errno.h>
 6 #include <sys/types.h>
 7
 8 void* thread_func1(void *arg);
 9 void* thread_func2(void *arg);
10
11 int main()
12 {
13     pthread_t tid1;
14     pthread_t tid2;
15     int       err;
16     void      *tret;
17     //创建新线程1
18     err = pthread_create(&tid1,NULL,thread_func1,NULL);
19     if(err != 0)
20     {
21         perror("pthread_create() error");
22         exit(-1);
23     }
24     //创建新线程2
25     err = pthread_create(&tid2,NULL,thread_func2,NULL);
26     if(err != 0)
27     {
28         perror("pthread_create() error");
29         exit(-1);
30     }
31     //等待线程1终止
32     err = pthread_join(tid1,&tret);
33     if(err != 0)
34     {
35         perror("pthread_join error");
36         exit(-1);
37     }
38     printf("thread1 exit code %d\n",(int)tret);
39     //等待线程2终止
40     err = pthread_join(tid2,&tret);
41     if(err != 0)
42     {
43         perror("pthread_join error");
44         exit(-1);
45     }
46     printf("thread2 exit code %d\n",(int)tret);
47     exit(0);
48 }
49 void* thread_func1(void *arg)
50 {
51     printf("thread1 is returning.\n");
52     return ((void*)1);
53 }
54 void* thread_func2(void *arg)
55 {
56     printf("thread2 exiting.\n");
57     pthread_exit((void*)2);
58 }

 程序执行结果如下:

需要注意的是pthread_create和pthread_exit函数的无类型指针参数能够传递的数值可以不止一个,该指针可以传递包含复杂信息的结构地址,这个结构必须所使用的内存必须在调用者用完以后必须仍然有效,否则会出现无法访问或非法。例如在线程的栈上分配了该结构,例如下面程序,参数不正确使用,导致结果错误,程序如下:

 1 #include <pthread.h>
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <unistd.h>
 5 #include <errno.h>
 6 #include <sys/types.h>
 7
 8 struct foo
 9 {
10     int a;
11     int b;
12     int c;
13     int d;
14 };
15 void printfoo(const char* str,const struct foo *fp);
16 void* thread_func1(void *arg);
17 void* thread_func2(void *arg);
18
19 int main()
20 {
21     pthread_t tid1;
22     pthread_t tid2;
23     int       err;
24     struct foo *pfoo;
25     err = pthread_create(&tid1,NULL,thread_func1,NULL);
26     if(err != 0)
27     {
28         perror("pthread_create() error");
29         exit(-1);
30     }
31     err = pthread_join(tid1,(void*)&pfoo);
32     if(err != 0)
33     {
34         perror("pthread_join error");
35         exit(-1);
36     }
37     sleep(1);
38     printf("Parent starting second thread.\n");
39     err = pthread_create(&tid2,NULL,thread_func2,NULL);
40     if(err != 0)
41     {
42         perror("pthread_create() error");
43         exit(-1);
44     }
45     sleep(1);
46     printfoo("parent: ",pfoo);
47     exit(0);
48 }
49 void printfoo(const char* str,const struct foo *fp)
50 {
51     puts(str);
52     printf(" structure at 0x%x\n",(unsigned int)fp);
53     printf("foo.a = %d\n",fp->a);
54     printf("foo.b = %d\n",fp->b);
55     printf("foo.c = %d\n",fp->c);
56     printf("foo.d = %d\n",fp->d);
57 }
58
59 void* thread_func1(void *arg)
60 {
61     struct foo f = {1,2,3,4};
62     printfoo("thread1:",&f);
63     pthread_exit((void*)&f);
64 }
65 void* thread_func2(void *arg)
66 {
67     printf("thread2: ID is %u\n",(unsigned int)pthread_self());
68     pthread_exit((void*)2);
69 }

程序执行结果如下:

从结果可以看出,创建第一个线程时候,在该线程的栈中创建了一个结构,第二个线程栈覆盖了第一个线程栈,可以通过使用全局变量结构或者是使用malloc动态分配结构。

      线程调用pthread_cancel函数来请求取消同一进程中的其他线程,并不等待线程终止,只是提出请求而已。函数原型为 int pthread_cancel(pthread_t tid)。函数功能等价于使得tid标识的线程调用pthread_exit(PTHREAD_CANCELED)。

  线程清理处理程序,类似进程退出时候清理函数,线程可以建立多个清理处理程序,处理程序记录在栈中,执行顺序与注册顺序相反。函数原型如下:

  void pthread_cleanup_push(void (*routine)(void *),void *arg);   //注册清理函数
void pthread_cleanup_pop(int execute);  //删除清理程序,若execute=0,清理函数将不被调用

  两个函数限制:必须在线程相同的作用域中以匹配队的形式使用

清理函数在以下三种情况会调用:(1)调用pthread_exit;(2)响应取消请求;(3)用非零execute参数调用pthread_cleanup_pop。pthread_cleanup_pop函数删除上次pthread_cleanup_push调用建立的清理处理程序。

写个程序测试调用线程清理程序,程序如下:

 1 #include <pthread.h>
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <unistd.h>
 5 #include <errno.h>
 6 #include <sys/types.h>
 7
 8 static void cleanup(void *arg);  //线程清理函数
 9 void* thread_func1(void *arg);
10 void* thread_func2(void *arg);
11
12 int main()
13 {
14     pthread_t tid1;
15     pthread_t tid2;
16     int       err;
17     void   *tret;
18     //创建线程1
19     err = pthread_create(&tid1,NULL,thread_func1,(void*)1);
20     if(err != 0)
21     {
22         perror("pthread_create() error");
23         exit(-1);
24     }
25     //创建线程2
26     err = pthread_create(&tid2,NULL,thread_func2,(void*)1);
27     if(err != 0)
28     {
29         perror("pthread_create() error");
30         exit(-1);
31     }
32     //等待线程1结束
33     err = pthread_join(tid1,&tret);
34     if(err != 0)
35     {
36         perror("pthread_join error");
37         exit(-1);
38     }
39     printf("thread 1 exit code %d\n",(int)tret);
40     //等待线程2结束
41     err = pthread_join(tid2,&tret);
42     if(err != 0)
43     {
44         perror("pthread_join error");
45         exit(-1);
46     }
47     printf("thread 2 exit code %d\n",(int)tret);
48     exit(0);
49 }
50
51 static void cleanup(void *arg)
52 {
53     printf("cleanup: %s\n",(char*)arg);
54 }
55 void* thread_func1(void *arg)
56 {
57    printf("Thread 1 start.\n");
58    pthread_cleanup_push(cleanup,"thread 1 first handler");
59    pthread_cleanup_push(cleanup,"thread 1 second handler");
60    printf("thread 1 push complete.\n");
61    if(arg)
62     return ((void*)1);   //返回终止,将不会调用清理处理程序
63    pthread_cleanup_pop(0);
64    pthread_cleanup_pop(0);
65    return ((void*)1);
66 }
67 void* thread_func2(void *arg)
68 {
69    printf("Thread 2 start.\n");
70    pthread_cleanup_push(cleanup,"thread 2 first handler");
71    pthread_cleanup_push(cleanup,"thread 2 second handler");
72    printf("thread 2 push complete.\n");
73    if(arg)
74      pthread_exit((void*)2); //会调用清理处理程序
75    pthread_cleanup_pop(0);
76    pthread_cleanup_pop(0);
77    pthread_exit((void*)2);
78 }

 程序执行结果如下:

从结果可以看出线程1的清理处理程序没有被调用,线程2的清理处理程序调用序列与注册序列相反。

进程原语与线程原语的比较

进程原语 线程原语 描述
fork pthread_create 创建新的控制流
exit pthread_exit 从现有的控制流中退出
waitpid pthread_join 从控制流中得到退出状态
atexit pthread_cleanup_push 注册在退出控制流时调用的函数
getpid pthread_self 获取控制流的ID
abort pthread_cancel 请求控制流的非正常退出

4、线程同步

  当多个控制线程共享相同的内存时,需要确保每个线程看到一致的数据视图。只有多个线程存在同时读写同一变量时,需要对线程进行同步 。线程同步的方法:线程锁(互斥量)、读写锁、条件变量。

如果对多个线程访问共同数据时,不加同步控制,会出什么问题呢?如下程序,两个线程对一个共享的数据结构进行操作,结果是不确定的。

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <errno.h>
 5 #include <sys/types.h>
 6 #include <pthread.h>
 7
 8 struct foo
 9 {
10     int          f_count;
11 };
12
13 struct foo* foo_alloc(void);
14 void foo_add(struct foo *fp);
15 void foo_release(struct foo *fp);
16
17 void * thread_func1(void *arg);
18 void * thread_func2(void *arg);
19 int main()
20 {
21     pthread_t pid1,pid2;
22     int err;
23     void *pret;
24     struct foo *fobj;
25     fobj = foo_alloc();
26     //创建新线程1,函数地址为thread_fun1,参数为fobj
27     err = pthread_create(&pid1,NULL,thread_func1,(void*)fobj);
28     if(err != 0)
29     {
30         perror("pthread_create() error");
31         exit(-1);
32     }
33     ////创建新线程2,函数地址为thread_fun2,参数为fobj
34     err = pthread_create(&pid2,NULL,thread_func2,(void*)fobj);
35     if(err != 0)
36     {
37         perror("pthread_create() error");
38         exit(-1);
39     }
40     //等待线程退出
41     pthread_join(pid1,&pret);
42     printf("thread 1 exit code is: %d\n",(int)pret);
43     pthread_join(pid2,&pret);
44     printf("thread 2 exit code is: %d\n",(int)pret);
45     exit(0);
46 }
47 //初始化
48 struct foo* foo_alloc(void)
49 {
50     struct foo *fobj;
51     fobj = (struct foo*)malloc(sizeof(struct foo));
52     if(fobj != NULL)
53         fobj->f_count = 0;
54     return fobj;
55 }
56 void foo_add(struct foo *fp)
57 {
58     fp->f_count++;
59     printf("f_count = %d\n",fp->f_count);
60 }
61 void foo_release(struct foo *fp)
62 {
63     fp->f_count--;
64     printf("f_count = %d\n",fp->f_count);
65 }
66 void * thread_func1(void *arg)
67 {
68     struct foo *fp = (struct foo*)arg;
69     printf("thread 1 start.\n");
70     foo_release(fp);  //数目减少1
71     printf("thread 1 exit.\n");
72     pthread_exit((void*)1);
73 }
74 void * thread_func2(void *arg)
75 {
76     struct foo *fp = (struct foo*)arg;
77     printf("thread 2 start.\n");
78     foo_add(fp); //数目增加1
79     foo_add(fp);
80     printf("thread 2 exit.\n");
81     pthread_exit((void*)2);
82 }

执行结果如下:

从结果可以看出:程序执行两次结果是不一样的,随即性比较强。线程1和线程2执行顺序是不确定的。需要带线程1和线程2进行同步控制。

(1)互斥量

  mutex是一种简单的加锁的方法来控制对共享资源的访问。在同一时刻只能有一个线程掌握某个互斥上的锁,拥有上锁状态的线程能够对共享资源进行访问。若其他线程希望上锁一个已经被上了互斥锁的资源,则该线程挂起,直到上锁的线程释放互斥锁为止。互斥量类型为pthread_mutex_t。互斥量操作函数如下:

int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutex_attr_t *mutexattr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_lock(pthread_mutex_t *mutex);   //对互斥量进行加锁
int pthread_mutex_trylock(pthread_mutex_t *mutex); //尝试对互斥量进行加锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);  //对互斥量进行解锁

写个程序练习一个互斥量,对以上程序添加互斥量,创建两个线程操作一个数据结构,修改公共的数据。程序如下:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <errno.h>
 5 #include <sys/types.h>
 6 #include <pthread.h>
 7
 8 struct foo
 9 {
10     int                         f_count;
11     pthread_mutex_t     f_lock; //互斥量
12 };
13
14 struct foo* foo_alloc(void);
15 void foo_add(struct foo *fp);
16 void foo_release(struct foo *fp);
17
18 void * thread_func1(void *arg);
19 void * thread_func2(void *arg);
20 int main()
21 {
22     pthread_t pid1,pid2;
23     int err;
24     void *pret;
25     struct foo *fobj;
26     fobj = foo_alloc();
27     err = pthread_create(&pid1,NULL,thread_func1,(void*)fobj);
28     if(err != 0)
29     {
30         perror("pthread_create() error");
31         exit(-1);
32     }
33     err = pthread_create(&pid2,NULL,thread_func2,(void*)fobj);
34     if(err != 0)
35     {
36         perror("pthread_create() error");
37         exit(-1);
38     }
39     pthread_join(pid1,&pret);
40     printf("thread 1 exit code is: %d\n",(int)pret);
41     pthread_join(pid2,&pret);
42     printf("thread 2 exit code is: %d\n",(int)pret);
43     exit(0);
44 }
45 struct foo* foo_alloc(void)
46 {
47     struct foo *fobj;
48     fobj = (struct foo*)malloc(sizeof(struct foo));
49     if(fobj != NULL)
50     {
51         fobj->f_count = 0;
52         //初始化互斥量
53         if (pthread_mutex_init(&fobj->f_lock,NULL) != 0)
54         {
55             free(fobj);
56             return NULL;
57         }
58     }
59     return fobj;
60 }
61 void foo_add(struct foo *fp)
62 {
63     pthread_mutex_lock(&fp->f_lock);  //加锁
64     fp->f_count++;
65     printf("f_count = %d\n",fp->f_count);
66     pthread_mutex_unlock(&fp->f_lock); //解锁
67 }
68 void foo_release(struct foo *fp)
69 {
70     pthread_mutex_lock(&fp->f_lock);  //加锁
71     fp->f_count--;
72     printf("f_count = %d\n",fp->f_count);
73     if(fp->f_count == 0)
74     {
75         pthread_mutex_unlock(&fp->f_lock);  //解锁
76         pthread_mutex_destroy(&fp->f_lock);  //是否锁
77         free(fp);
78     }
79     else
80          pthread_mutex_unlock(&fp->f_lock);  //解锁
81 }
82 void * thread_func1(void *arg)
83 {
84     struct foo *fp = (struct foo*)arg;
85     printf("thread 1 start.\n");
86     foo_release(fp);
87     printf("thread 1 exit.\n");
88     pthread_exit((void*)1);
89 }
90 void * thread_func2(void *arg)
91 {
92     struct foo *fp = (struct foo*)arg;
93     printf("thread 2 start.\n");
94     foo_add(fp);
95     foo_add(fp);
96      printf("thread 2 exit.\n");
97     pthread_exit((void*)2);
98 }

程序执行结果如下:

从结果可以看出,程序执行多次结果相同,线程1和线程2同步操作。

(2)读写锁(共享锁)

  读写锁可以使读操作比互斥量有更高的并行性,互斥量要么是锁住状态要么是不加锁状态,而且一次只有一个线程可以对其加锁。读写锁有三种状态:读模式下加锁状态、写模式下加锁状态、不加锁状态。一次只有一个线程可以占有写模式读写锁,而多个线程可以同时占有度模式的读写锁。当读操作较多,写操作较少时,可使用读写锁提高线程读并发性。读写锁数据类型为pthread_rwlock_t,操作函数如下:

int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_mutex_t *mutex); 
int pthread_rwlock_trywrlock(pthread_mutex_t *mutex); 

写个程序练习读写锁,创建三个线程,两个线程读操作,一个线程写操作。程序如下:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <errno.h>
 5 #include <sys/types.h>
 6 #include <pthread.h>
 7 struct foo
 8 {
 9     int                 f_count;
10     pthread_rwlock_t    f_rwlock;
11 };
12 struct foo * foo_alloc()
13 {
14     struct foo* fp;
15     fp = (struct foo*)malloc(sizeof(struct foo));
16     if(fp != NULL)
17     {
18         fp->f_count = 0;
19         pthread_rwlock_init(&fp->f_rwlock,NULL);
20     }
21     return fp;
22 }
23 void foo_add(struct foo *fp)
24 {
25     pthread_rwlock_wrlock(&fp->f_rwlock);
26     fp->f_count++;
27     pthread_rwlock_unlock(&fp->f_rwlock);
28 }
29 void foo_release(struct foo *fp)
30 {
31     pthread_rwlock_wrlock(&fp->f_rwlock);
32     fp->f_count--;
33     if(fp->f_count == 0)
34     {
35         pthread_rwlock_unlock(&fp->f_rwlock);
36         pthread_rwlock_destroy(&fp->f_rwlock);
37         free(fp);
38     }
39     pthread_rwlock_unlock(&fp->f_rwlock);
40 }
41 int foo_search(struct foo *fp)
42 {
43     int count;
44     pthread_rwlock_rdlock(&fp->f_rwlock);
45     count = fp->f_count;
46     pthread_rwlock_unlock(&fp->f_rwlock);
47     return count;
48 }
49 void * thread_func1(void *arg)
50 {
51     struct foo *fp = (struct foo*)arg;
52     printf("thread 1 start.\n");
53     foo_search(fp);
54     printf("in thread 1 search count = %d\n",fp->f_count);
55     printf("thread 1 exit.\n");
56     pthread_exit((void*)1);
57 }
58 void * thread_func2(void *arg)
59 {
60     struct foo *fp = (struct foo*)arg;
61     printf("thread 2 start.\n");
62     foo_add(fp);
63     printf("in thread 2 add count = %d\n",fp->f_count);
64     printf("thread 2 exit.\n");
65     pthread_exit((void*)2);
66 }
67 void * thread_func3(void *arg)
68 {
69     struct foo *fp = (struct foo*)arg;
70     printf("thread 3 start.\n");
71     foo_add(fp);
72     printf("in thread 3 add count = %d\n",fp->f_count);
73     foo_search(fp);
74     printf("in thread 3 search count = %d\n",fp->f_count);
75     printf("thread 3 exit.\n");
76     pthread_exit((void*)3);
77 }
78
79 int main()
80 {
81     pthread_t pid1,pid2,pid3;
82     int err;
83     void *pret;
84     struct foo *fobj;
85     fobj = foo_alloc();
86     pthread_create(&pid1,NULL,thread_func1,(void*)fobj);
87     pthread_create(&pid2,NULL,thread_func2,(void*)fobj);
88     pthread_create(&pid3,NULL,thread_func3,(void*)fobj);
89     pthread_join(pid1,&pret);
90     printf("thread 1 exit code is: %d\n",(int)pret);
91     pthread_join(pid2,&pret);
92     printf("thread 2 exit code is: %d\n",(int)pret);
93     pthread_join(pid3,&pret);
94     printf("thread 3 exit code is: %d\n",(int)pret);
95     exit(0);
96 }

 程序执行结果如下:

(3)条件变量

  条件变量给多个线程提供了个会合的机会,条件变量与互斥量一起使用,允许线程以无竞争的方式等待特定的条件发生,条件本身是由互斥量保护。线程在改变条件状态前必须先锁住互斥量,条件变量允许线程等待特定条件发生。条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般说来,条件变量被用来进行线承间的同步。条件变量类型为pthread_cond_t,使用前必须进行初始化,操作函数如下:

int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex); 
int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,const structtimespec *restrict abstime);
int pthread_cond_broadcast(pthread_cond_t *cond);  //唤醒等待该条件的所有线程
int pthread_cond_signal(pthread_cond_t *cond); //唤醒等待该条件的某个线程

写个程序练习条件变量,程序中创建两个新线程1和2,线程1使数目增加,线程2使数目减少。只有当数目不为0时,减少才能进行,为0时候需要等待。程序如下:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <errno.h>
 5 #include <sys/types.h>
 6 #include <pthread.h>
 7
 8 struct foo
 9 {
10     int                 f_count;
11     pthread_mutex_t     f_mutex;
12     pthread_cond_t      f_cond;
13 };
14
15 struct foo * foo_alloc()
16 {
17     struct foo* fp;
18     fp = (struct foo*)malloc(sizeof(struct foo));
19     if(fp != NULL)
20     {
21         fp->f_count = 0;
22         pthread_mutex_init(&fp->f_mutex,NULL);
23         pthread_cond_init(&fp->f_cond,NULL);
24     }
25     return fp;
26 }
27 void foo_increase(struct foo *fp)
28 {
29     pthread_mutex_lock(&fp->f_mutex);
30     if(fp->f_count== 0)  //发送信号,通知可以做decrease操作
31         pthread_cond_signal(&fp->f_cond);
32     fp->f_count++;
33     pthread_mutex_unlock(&fp->f_mutex);
34 }
35 void foo_decrease(struct foo *fp)
36 {
37     pthread_mutex_lock(&fp->f_mutex);
38     while(fp->f_count == 0)  //判断是否可以做decrease操作
39         pthread_cond_wait( &fp->f_cond, &fp->f_mutex);//等待信号
40     fp->f_count--;
41     pthread_mutex_unlock(&fp->f_mutex);
42 }
43 void * thread_func1(void *arg)
44 {
45     struct foo *fp = (struct foo*)arg;
46     printf("thread 1 start.\n");
47     foo_increase(fp);
48     printf("in thread 1 count = %d\n",fp->f_count);
49     printf("thread 1 exit.\n");
50     pthread_exit((void*)1);
51 }
52 void * thread_func2(void *arg)
53 {
54     struct foo *fp = (struct foo*)arg;
55     printf("thread 2 start.\n");
56     foo_decrease(fp);
57     printf("in thread 2  count = %d\n",fp->f_count);
58     printf("thread 2 exit.\n");
59     pthread_exit((void*)2);
60 }
61
62 int main()
63 {
64     pthread_t pid1,pid2,pid3;
65     int err;
66     void *pret;
67     struct foo *fobj;
68     fobj = foo_alloc();
69     pthread_create(&pid1,NULL,thread_func1,(void*)fobj);
70     pthread_create(&pid2,NULL,thread_func2,(void*)fobj);
71     pthread_join(pid1,&pret);
72     printf("thread 1 exit code is: %d\n",(int)pret);
73     pthread_join(pid2,&pret);
74     printf("thread 2 exit code is: %d\n",(int)pret);
75     exit(0);
76 }

程序执行结果如下:

时间: 2024-12-22 03:43:38

《APUE》读书笔记—第十一章线程的相关文章

java编程思想-java编程四线第二十一章 线程SynchronizationComparisons.java有错误

问题描述 java编程四线第二十一章 线程SynchronizationComparisons.java有错误 //BaseLine 和AtomicTest 是线程不安全的 ,求解答 //: concurrency/SynchronizationComparisons.java// Comparing the performance of explicit Locks// and Atomics versus the synchronized keyword.import java.util.c

MYSQL必知必会读书笔记第三章之显示数据库_Mysql

MySQL是一种开放源代码的关系型数据库管理系统(RDBMS),MySQL数据库系统使用最常用的数据库管理语言--结构化查询语言(SQL)进行数据库管理. show column from tablename: 对每一个字段返回一行,行中包含字段名,数据类型.是否允许NULL.键信息.默认值以及其他信息. describe 语句: MySQL支持使用describ作为show columns from 的一种快捷方式.describ tablename 所支持的其他的show语句: show s

MYSQL必知必会读书笔记第六章之过滤数据_Mysql

mysql简介 MySQL是一种开放源代码的关系型数据库管理系统(RDBMS),MySQL数据库系统使用最常用的数据库管理语言--结构化查询语言(SQL)进行数据库管理. where子句的位置,在同时使用ORDER BY 和WHERE子句时应该让ORDER BY 位于where之后,否则会产生错误. 1.不匹配检查 复制代码 代码如下: SELECT vend_id FROM products where vend_id <>1003 等同于 复制代码 代码如下: SELECT vend_id

MYSQL必知必会读书笔记第四章之检索数据_Mysql

MySQL是一种开放源代码的关系型数据库管理系统(RDBMS),MySQL数据库系统使用最常用的数据库管理语言--结构化查询语言(SQL)进行数据库管理. 使用Select语句返回的数据,可能会发现显示的数据会与其他的地方顺序不同.出现这种情况很正常.如果没有明确排序查询结果.则返回的数据没有特殊意义.返回数据的顺序可能是是数据被添加到表中的顺序,也可能不是.只要返回相同数目的行,就是正常. 注意:SQL语句和大小写 请注意,SQL语句不区分大小写,因此select和SELECT是相同的.同样,

《Linux内核设计与实现》读书笔记(十一)- 定时器和时间管理【转】

转自:http://www.cnblogs.com/wang_yb/archive/2013/05/10/3070373.html 系统中有很多与时间相关的程序(比如定期执行的任务,某一时间执行的任务,推迟一段时间执行的任务),因此,时间的管理对于linux来说非常重要.   主要内容: 系统时间 定时器 定时器相关概念 定时器执行流程 实现程序延迟的方法 定时器和延迟的例子   1. 系统时间 系统中管理的时间有2种:实际时间和定时器. 1.1  实际时间 实际时间就是现实中钟表上显示的时间,

【PMP】Head First PMP 学习笔记 第十一章 风险管理

第十一章 风险管理 计划再仔细的项目也会遇到麻烦. 什么是风险 风险是任何可能影响项目的不确定时间或状况,但并非所有风险都是负面的 风险(risk) 事件(event) 状况(condition) 机会(opporunity) 风险偏好.为了预期的回报,一个实体愿意承受不确定的程度. 风险承受力.组织或个人能承受的风险程度.数量或容量. 风险临界值.干系人特别关注的特定的不确定程度或影响程度. 规划风险管理 规划风险管理过程在项目构思阶段就应开始,并在项目规划阶段的早期完成. 输入 项目管理计划

《APUE》读书笔记—第七章进程环境

本章主要介绍了Unix进程环境,包含main函数是如何被调用的,命令行参数如何传递,存储方式布局,分配存储空间,环境变量,进程终止方法,全局跳转longjmp和setjmp函数及进程的资源限制. main函数的原型为int main(int argc,char *argv[]);其中argc是命令行参数的数目,argv是指向参数的各个指针构成的数组.当内核执行C程序时,使用一个exec函数,在调用main函数前线调用一个特殊的启动例程,从内核获取命令行参数和环境变量. 进程终止分为正常终止和异常

《APUE》读书笔记-第十七章高级进程间通信

本章主要介绍了基于STREAM的管道和UNIX域套接字,这些IPC可以在进程间传送打开文件描述符.服务进程可以使用它们的打开文件描述符与指定的名字相关联,客户进程可以使用这些名字与服务器进程通信. 1.基于STREAMS的管道 STREAMS pipe是一个双向(全双工)管道,单个STREAMS管道就能向父.子进程提供双向的数据流.如下图所示: 下面采用STREAMS管道实现加法协同进程实例,程序如下: View Code 1 1 #include <stdio.h> 2 2 #include

《APUE》读书笔记—第四章文件和目录

本章主要介绍的是文件结构及目录.重点是通过stat函数获取文件的结构信息,然后是文件目录及其遍历.学完本章后,编写了一个输出给的目录下的文件信息的程序. 首先是包含在<sys/stat.h>文件下的stat.fstat.lstat三个函数,三个函数的原型如下: int stat(const char *path, struct stat *buf); int fstat(int fd, struct stat *buf); int lstat(const char *path, struct