岳阳网站开发网站运营,网站建设辶金手指排名十二,代理服务器ip免费,重庆点优定制网站建设http://blog.csdn.net/ljianhui/article/details/10813469/ 信号量、同步这些名词在进程间通信时就已经说过#xff0c;在这里它们的意思是相同的#xff0c;只不过是同步的对象不同而已。但是下面介绍的信号量的接口是用于线程的信号量#xff0c;注意不要跟用于进程间通信…http://blog.csdn.net/ljianhui/article/details/10813469/ 信号量、同步这些名词在进程间通信时就已经说过在这里它们的意思是相同的只不过是同步的对象不同而已。但是下面介绍的信号量的接口是用于线程的信号量注意不要跟用于进程间通信的信号量混淆关于用于进程间通信的信号量的详细介绍可以参阅我的另一篇博文Linux进程间通信——使用信号量。相似地线程同步是控制线程执行和访问临界区域的方法。一、什么是信号量线程的信号量与进程间通信中使用的信号量的概念是一样它是一种特殊的变量它可以被增加或减少但对其的关键访问被保证是原子操作。如果一个程序中有多个线程试图改变一个信号量的值系统将保证所有的操作都将依次进行。而只有0和1两种取值的信号量叫做二进制信号量在这里将重点介绍。而信号量一般常用于保护一段代码使其每次只被一个执行线程运行。我们可以使用二进制信号量来完成这个工作。二、信号量的接口和使用信号量的函数都以sem_开头线程中使用的基本信号量函数有4个它们都声明在头文件semaphore.h中。1、sem_init函数该函数用于创建信号量其原型如下[cpp] view plain copy int sem_init(sem_t *sem, int pshared unsigned int value); 该函数初始化由sem指向的信号对象设置它的共享选项并给它一个初始的整数值。pshared控制信号量的类型如果其值为0就表示这个信号量是当前进程的局部信号量否则信号量就可以在多个进程之间共享value为sem的初始值。调用成功时返回0失败返回-1.2、sem_wait函数该函数用于以原子操作的方式将信号量的值减1。原子操作就是如果两个线程企图同时给一个信号量加1或减1它们之间不会互相干扰。它的原型如下[cpp] view plain copy int sem_wait(sem_t *sem); sem指向的对象是由sem_init调用初始化的信号量。调用成功时返回0失败返回-1.3、sem_post函数该函数用于以原子操作的方式将信号量的值加1。它的原型如下[cpp] view plain copy int sem_post(sem_t *sem); 与sem_wait一样sem指向的对象是由sem_init调用初始化的信号量。调用成功时返回0失败返回-1.4、sem_destroy函数该函数用于对用完的信号量的清理。它的原型如下[cpp] view plain copy int sem_destroy(sem_t *sem); 成功时返回0失败时返回-1.三、使用信号量同步线程下面以一个简单的多线程程序来说明如何使用信号量进行线程同步。在主线程中我们创建子线程并把数组msg作为参数传递给子线程然后主线程等待直到有文本输入然后调用sem_post来增加信号量的值这样就会立刻使子线程从sem_wait的等待中返回并开始执行。线程函数在把字符串的小写字母变成大写并统计输入的字符数量之后它再次调用sem_wait并再次被阻塞直到主线程再次调用sem_post增加信号量的值。[cpp] view plain copy #include unistd.h #include pthread.h #include semaphore.h #include stdlib.h #include stdio.h #include string.h //线程函数 void *thread_func(void *msg); sem_t sem;//信号量 #define MSG_SIZE 512 int main() { int res -1; pthread_t thread; void *thread_result NULL; char msg[MSG_SIZE]; //初始化信号量其初值为0 res sem_init(sem, 0, 0); if(res -1) { perror(semaphore intitialization failed\n); exit(EXIT_FAILURE); } //创建线程并把msg作为线程函数的参数 res pthread_create(thread, NULL, thread_func, msg); if(res ! 0) { perror(pthread_create failed\n); exit(EXIT_FAILURE); } //输入信息以输入end结束由于fgets会把回车\n也读入所以判断时就变成了“end\n” printf(Input some text. Enter endto finish...\n); while(strcmp(end\n, msg) ! 0) { fgets(msg, MSG_SIZE, stdin); //把信号量加1 sem_post(sem); } printf(Waiting for thread to finish...\n); //等待子线程结束 res pthread_join(thread, thread_result); if(res ! 0) { perror(pthread_join failed\n); exit(EXIT_FAILURE); } printf(Thread joined\n); //清理信号量 sem_destroy(sem); exit(EXIT_SUCCESS); } void* thread_func(void *msg) { //把信号量减1 sem_wait(sem); char *ptr msg; while(strcmp(end\n, msg) ! 0) { int i 0; //把小写字母变成大写 for(; ptr[i] ! \0; i) { if(ptr[i] a ptr[i] z) { ptr[i] - a - A; } } printf(You input %d characters\n, i-1); printf(To Uppercase: %s\n, ptr); //把信号量减1 sem_wait(sem); } //退出线程 pthread_exit(NULL); } 运行结果如下从运行的结果来看这个程序的确是同时在运行两个线程一个控制输入另一个控制处理统计和输出。四、分析此信号量同步程序的缺陷但是这个程序有一点点的小问题就是这个程序依赖接收文本输入的时间足够长这样子线程才有足够的时间在主线程还未准备好给它更多的单词去处理和统计之前处理和统计出工作区中字符的个数。所以当我们连续快速地给它两组不同的单词去统计时子线程就没有足够的时间支执行但是信号量已被增加不止一次所以字符统计线程子线程就会反复处理和统计字符数目并减少信号量的值直到它再次变成0为止。为了更加清楚地说明上面所说的情况修改主线程的while循环中的代码如下 [cpp] view plain copy printf(Input some text. Enter endto finish...\n); while(strcmp(end\n, msg) ! 0) { if(strncmp(TEST, msg, 4) 0) { strcpy(msg, copy_data\n); sem_post(sem); } fgets(msg, MSG_SIZE, stdin); //把信号量加1 sem_post(sem); } 重新编译程序此时运行结果如下 当我们输入TEST时主线程向子线程提供了两个输入一个是来自键盘的输入一个来自主线程复数据到msg中然后从运行结果可以看出运行出现了异常没有处理和统计从键盘输入TEST的字符串而却对复制的数据作了两次处理。原因如上面所述。五、解决此缺陷的方法解决方法有两个一个就是再增加一个信号量让主线程等到子线程处理统计完成之后再继续执行另一个方法就是使用互斥量。下面给出用增加一个信号量的方法来解决该问题的代码源文件名为semthread2.c源代码如下[cpp] view plain copy #include unistd.h #include pthread.h #include semaphore.h #include stdlib.h #include stdio.h #include string.h //线程函数 void *thread_func(void *msg); sem_t sem;//信号量 sem_t sem_add;//增加的信号量 #define MSG_SIZE 512 int main() { int res -1; pthread_t thread; void *thread_result NULL; char msg[MSG_SIZE]; //初始化信号量,初始值为0 res sem_init(sem, 0, 0); if(res -1) { perror(semaphore intitialization failed\n); exit(EXIT_FAILURE); } //初始化信号量,初始值为1 res sem_init(sem_add, 0, 1); if(res -1) { perror(semaphore intitialization failed\n); exit(EXIT_FAILURE); } //创建线程并把msg作为线程函数的参数 res pthread_create(thread, NULL, thread_func, msg); if(res ! 0) { perror(pthread_create failed\n); exit(EXIT_FAILURE); } //输入信息以输入end结束由于fgets会把回车\n也读入所以判断时就变成了“end\n” printf(Input some text. Enter endto finish...\n); sem_wait(sem_add); while(strcmp(end\n, msg) ! 0) { if(strncmp(TEST, msg, 4) 0) { strcpy(msg, copy_data\n); sem_post(sem); //把sem_add的值减1即等待子线程处理完成 sem_wait(sem_add); } fgets(msg, MSG_SIZE, stdin); //把信号量加1 sem_post(sem); //把sem_add的值减1即等待子线程处理完成 sem_wait(sem_add); } printf(Waiting for thread to finish...\n); //等待子线程结束 res pthread_join(thread, thread_result); if(res ! 0) { perror(pthread_join failed\n); exit(EXIT_FAILURE); } printf(Thread joined\n); //清理信号量 sem_destroy(sem); sem_destroy(sem_add); exit(EXIT_SUCCESS); } void* thread_func(void *msg) { char *ptr msg; //把信号量减1 sem_wait(sem); while(strcmp(end\n, msg) ! 0) { int i 0; //把小写字母变成大写 for(; ptr[i] ! \0; i) { if(ptr[i] a ptr[i] z) { ptr[i] - a - A; } } printf(You input %d characters\n, i-1); printf(To Uppercase: %s\n, ptr); //把信号量加1表明子线程处理完成 sem_post(sem_add); //把信号量减1 sem_wait(sem); } sem_post(sem_add); //退出线程 pthread_exit(NULL); } 其运行结果如下分析这里我们多使用了一个信号量sem_add并把它的初值赋为1在主线程在使用sem_wait来等待子线程处理完全由于它的初值为1所以主线程第一次调用sem_wait总是立即返回而第二次调用则需要等待子线程处理完成之后。而在子线程中若处理完成就会马上使用sem_post来增加信号量的值使主线程中的sem_wait马上返回并执行紧接下面的代码。从运行结果来看运行终于正常了。注意在线程函数中信号量sem和sem_add使用sem_wait和sem_post函数的次序它们的次序不能错乱否则在输入end时可能运行不正常子线程不能正常退出从而导致程序不能退出。至于使用互斥量的方法将会在下篇文章Linux多线程——使用互斥量同步线程中详细介绍。