1.信号初始化:
- #include <semaphore.h>
sem_init(&sem[0],int pshared,unsigned int value);
-pshared:
0:给线程使用
!0:可以给进程使用
-value:初始化值
2.p操作
(a)函数原型
- #include <semaphore.h>
int sem_wait(sem_t *sem);//阻塞p操作
功能:阻塞p操作集合中某个信号量,值-1
如果能够p操作成功最好,否则就阻塞直到p操作操作成功为止。
返回值:成功返回0,失败返回-1,errno被设置。
参数:p操作的某个信号量。
比如:sem_wait(&sem[0]);
sem_wait的兄弟函数:
- int sem_trywait(sem_t *sem):不阻塞
如果能够p操作就p操作,如果不能p操作就出错返回,不会阻塞。 - int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
可以设置阻塞时间,如果能够p操作就p操作,不能就阻塞,如果在设置的时间内好没有p操作成功就是出错返回,不再阻塞。
3.v操作
(a)函数原型 - #include <semaphore.h>
int sem_post(sem_t *sem);
功能:对某个信号量进行v操作,v操作不存在阻塞问题。v操作成功后,信号量的值会+1
返回值:成功返回0,失败返回-1,errno被设置。(b)代码演示
sem_post(&sem[0]);
4. 初始化条件变量
函数原型:
- #include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
功能:初始化条件变量,与互斥锁的初始化类似。
- pthread_cond_t cond; //定义条件变量
pthread_cond_init(&cond, NULL); //第二个参数为NULL,表示不设置条件变量的属性。
也可以直接初始化:
pthread_cond_t cond =PTHREAD_COND_INITIALIZER;//与互斥锁的初始化的原理是一样的
返回值:成功返回0,失败返回非零错误号
参数:
- cond:条件变量
- attr:用于设置条件变量的属性,设置为NULL,表示使用默认属性
5.进程 VS 线程:
(1)多线程比多进程成本低,但性能更低 :
多进程是立体交通系统,虽然造价高,上坡下坡多耗点油,但是不堵车。
多线程是平面交通系统,造价低,但红绿灯太多,老堵车。
(2) 区别:
-进程是资源分配的最小单位,线程是任务调度的最小单位;
-每个进程拥有独立的地址空间,多个线程共享进程地址空间:
- 线程之间切换比进程之间切换开销少;线程的调度必须通过频繁加锁来保持同步,影响了线程并发性能;
- 进程比线程更健壮,多进程之间相互独立,进程的异常对其他进程无影响,一个线程的崩溃可能影响其他线程或者整个程序;
- 线程之间的通信更方便(小数据量),同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难点;
- 多线程的代码结构比多进程代码结构易读;
(3)如何选择?
- 需要频繁创建销毁的优先用线程
- 高性能交易服务器中间件,如TUXEDO,都是主张多进程的
- 需要进行大量计算的优先使用线程
- 强相关的处理用线程,弱相关的处理用进程
- 多机分布的用进程,多核分布的用线程
示例代码:
周璇 20:57:19
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <pthread.h>
5 #include <string.h>
6 #include <errno.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <fcntl.h>
10 #include <signal.h>
11
12
13 #define SECON_PTH_NUMS 2 //次线程数量
14 #define PTHEXIT -1
15
16
17 /* 传递给线程的参数 */
18 typedef struct pthread_arg
19 {
20 pthread_t tid;//存放线程tid
21 int pthno;//我自己定义的编号
22 int fd;//文件描述符
23 }ptharg;
24
25
26
27 struct gloable_va
28 {
29 ptharg pth_arg[SECON_PTH_NUMS];//结构体数组,每个元素会被当做参数传递给对应的次线程
30 int pth_exit_flg[SECON_PTH_NUMS];//每个元素存放对应编号线程的退出状态
31 pthread_attr_t attr;//存放线程新属性
32 }glbva;
33
34 void print_err(char *str, int line, int err_no)
35 {
36 printf("%d, %s:%s", line, str, strerror(err_no));
37 exit(-1);
38 }
39
40 /* 线程退出处理函数 */
41 void pth_exit_deal(void *arg)
42 {
43 pthread_t tid = ((ptharg *)arg)->tid;
44
45 printf("!!! pthread %lu exit\n", tid);
46 }
47
48 void *pth_fun(void *pth_arg)
49 {
50 int fd = ((ptharg *)pth_arg)->fd;
51 int pthno = ((ptharg *)pth_arg)->pthno;
52 pthread_t tid = ((ptharg *)pth_arg)->tid;
53
54 //pthread_detach(pthread_self());//线程把自己分离出去
55
56 //注册线程退出处理函数
57 pthread_cleanup_push(pth_exit_deal, pth_arg);
58
59 printf("pthno=%d, pthread_id=%lu\n", pthno, tid);
60
61 while(1)
62 {
63 write(fd, "hello ", 6);
64 write(fd, "world\n", 6);
65 //检测退出状态
66 if(glbva.pth_exit_flg[pthno] == PTHEXIT) break;
67 }
68
69
70 pthread_cleanup_pop(!0);
71 return NULL;
72 pthread_exit((void *)10);
73 }
74
75 void signal_fun(int signo)
76 {
77 if(SIGALRM == signo)
78 {
79 int i = 0;
80 for(i=0; i<SECON_PTH_NUMS; i++)
81 {
82 //pthread_cancel(glbva.pth_arg[i].tid);//取消次线程
83 glbva.pth_exit_flg[i] = PTHEXIT;//设置为退出状态
84 }
85 }
86 else if(SIGINT == signo)
87 {
88 exit(0);
89 }
90 }
91
92 void process_exit_deal(void)
93 {
94 /* 销毁线程的属性设置 */
95 int ret = 0;
96 ret = pthread_attr_destroy(&glbva.attr);
97 if(ret != 0) print_err("pthread_attr_destroy fail", __LINE__, ret);
98
99 printf("\nprocess exit\n");
100 }
101
102 int main(void)
103 {
104 int fd = 0;
105 int i = 0;
106 int ret = 0;
107
108 //注册进程退出处理函数,exit正常终止进程时弹栈调用
109 atexit(process_exit_deal);
110
111 //打开文件,供线程操作,所有的线程(函数)可以共享打开的文件描述符
112 fd = open("./file", O_RDWR|O_CREAT|O_TRUNC, 0664);
113 if(fd == -1) print_err("open ./file fail", __LINE__, errno);
114
115 /* 初始化护attr, 设置一些基本的初始值 */
116 ret = pthread_attr_init(&glbva.attr);
117 if(ret != 0) print_err("pthread_attr_init fail", __LINE__, ret);
118
119 /* 设置分离属性 */
120 ret = pthread_attr_setdetachstate(&glbva.attr, PTHREAD_CREATE_DETACHED);
121 if(ret != 0) print_err("pthread_attr_setdetachstate fail", __LINE__, ret);
122
123 /* 通过循环创建两个次线程 */
124 for(i=0; i<SECON_PTH_NUMS; i++)
125 {
126 glbva.pth_arg[i].fd = fd;//保存文件描述符
127 glbva.pth_arg[i].pthno = i; //我自己给的线程编号
128 //创建好次线程后,讲次线程分离
129 ret = pthread_create(&glbva.pth_arg[i].tid, &glbva.attr, pth_fun, (void *)&glbva.pth_arg[i]);
130 if(ret != 0) print_err("pthread_create fail", __LINE__, ret);
131 }
132
133 printf("main tid = %lu\n", pthread_self());
134
135 signal(SIGINT, signal_fun);
136
137 /* 定时5秒,时间到后取消次线程 */
138 signal(SIGALRM, signal_fun);
139 alarm(3);
140
141 #if 0
142 void *retval = NULL;
143 for(i=0; i<SECON_PTH_NUMS; i++)
144 {
145 //阻塞等待此线程结束,回收次线程资源,并通过第二个参数接受返回值
146 pthread_join(glbva.pth_arg[i].tid, &retval);
147 printf("@@ %ld\n", (long)retval);
148 }
149 #endif
150
151
152 while(1)
153 {
154 write(fd, "hello ", 6);
155 write(fd, "world\n", 6);
156 }
157
158 return 0;
159 }