linux程序设计——多线程(第十二章)
12.8 多线程
之前,总是让程序的主线程只创建一个线程。这节将演示怎样在同一个程序中创建多个线程,然后怎样以不同于其启动顺序将它们合并在一起。此外,还演示多线程编程时easy出现的时序问题.
编敲代码thread8.c
/*************************************************************************
> File Name: thread8.c
> Description: thread8.c程序创建多个线程。然后以不同于启动顺序将它们合并在一起
> Author: Liubingbing
> Created Time: 2015年07月07日 星期二 19时37分45秒
> Other: thread8.c程序存在一个小漏洞,假设主线程执行足够快时,可能改动传递引用的參数thread_index,造成问题.见thread8a.c
************************************************************************/ #include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h> #define NUM_THREADS 6 void *thread_function(void *arg); int main(){
int res;
pthread_t a_thread[NUM_THREADS];
void *thread_result;
int thread_index; for (thread_index = 0; thread_index < NUM_THREADS; thread_index++) {
/* pthread_create创建新线程,这里创建了一个线程ID的数组 */
res = pthread_create(&(a_thread[thread_index]), NULL, thread_function, (void *)&thread_index);
if (res != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
sleep(1);
}
printf("Waiting for threads to finish...\n");
/* 主线程中等待合并这些子线程,但并非以创建它们的顺序来合并 */
for (thread_index = NUM_THREADS - 1; thread_index >= 0; thread_index--) {
res = pthread_join(a_thread[thread_index], &thread_result);
if (res == 0) {
printf("Picked up a thread\n");
} else {
perror("pthread_join failed");
}
}
printf("All done\n");
exit(EXIT_SUCCESS);
} void *thread_function(void *arg) {
int my_number = *(int *)arg;
int rand_num; printf("thread_function is running. Argument was %d\n", my_number);
/* 创建的线程等待一段随机的时间退出执行 */
rand_num = 1 + (int)(9.0 * rand() / (RAND_MAX + 1.0));
sleep(rand_num);
printf("Bye from %d\n", my_number);
pthread_exit(NULL);
}
执行thread8.c,看到例如以下结果:
这个程序首先创建一个线程ID的数组。例如以下所看到的:
pthread_t a_thread[NUM_THREADS];
然后通过循环创建多个线程。例如以下所看到的:
for (thread_index = 0; thread_index < NUM_THREADS; thread_index++) {
res = pthread_create(&(a_thread[thread_index]), NULL, thread_function, (void *)&thread_index);
}
创建出的线程等待一段随机的时间后退出执行,例如以下所看到的:
void *thread_function(void *arg) {
int my_number = *(int *) arg;
int rand_num;
printf("thread_function is running. Argument was %d\n", my_number);
rand_num = 1 + (int)(9.0 * rand() / RAND_MAX + 1.0));
sleep(rand_num);
printf("Bye from %d\n", my_number);
pthread_exit(NULL);
}
在主线程中。等待合并这些子线程。但并非以创建它们的顺序来合并。例如以下所看到的:
for (thread_index = NUM_THREADS -1; thread_index >= 0; thread_index--) {
res = pthread_join(a_thread[thread_index], &thread_result);
...
}
这个程序有一个小漏洞,假设将sleep调用从启动线程的循环中删除,它将会变得非常明显。
非常可能会看到一些奇怪的现象,比方一些线程以同样的參数被启动,类似下图:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
为什么会出现这种问题?启动线程时,线程函数的參数是一个局部变量,这个变量在循环中被更新。引起问题的代码行是:
for (thread_index = 0; thread_index < NUM_THREADS; thread_index++) {
res = pthread_create(&(a_thread[thread_index]), NULL, thread_function, (void *)&thread_index);
}
假设主线程执行的足够快(由于删除sleep(1)之后,主线程相对新线程就很快了),就可能改变某些线程的參数(即thread_index)。
此时,传递引用不是恰当的选择,而传值是正确的.当对共享变量和多个执行路径没有做到足够重视时,程序就可能出现这种错误行为。
编写线程程序时须要在设计上特别小心。
要改正这个问题。能够直接传递给这个參数的值,例如以下所看到的:
res = pthread_create(&(a_thread[thread_index]), NULL, thread_function, (void *)thread_index);
还有改动thread_function函数,例如以下所看到的:
int my_number = (int) arg;
linux程序设计——多线程(第十二章)的更多相关文章
- “全栈2019”Java多线程第二十二章:饥饿线程(Starvation)详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- “全栈2019”Java多线程第十二章:后台线程setDaemon()方法详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- 鸟哥的linux私房菜——第十二章学习(Shell Scripts)
第十二章 Shell Scripts 1.0).什么是shell scripts? script 是"脚本.剧本"的意思.整句话是说, shell script 是针对 shel ...
- 鸟哥的Linux私房菜——第十二章:档案的压缩与打包
视频链接: 土豆:http://www.tudou.com/programs/view/GncwT0FJKsQ B站(推荐):http://www.bilibili.com/video/av98857 ...
- JavaScript高级程序设计:第十二章
DOM1级主要定义的是HTML和XML文档的底层结构.DOM2和DOM3级则在这个结构的基础上引入了更多的交互能力,也支持了更高级的XML特性.为此DOM2和DOM3级分为许多模块,这些模块如下: D ...
- linux高级管理第十二章--rsync
实验部分 1.安装rsync 2.配置文件 3.配置密码 4.后续 5.为了测试,创建几个文件 配置实时同步 1.调整inotify内核参数 安装inotify-tools 测试同步 编写脚本 验证 ...
- Linux学习笔记(第十二章)
grep进阶 grep:以整行为单位进行截取 截取的特殊符号 正规表示法特殊字符 注意: sed用法 格式化打印 awk 用法 diff档案对比: path旧文档升级为新文档
- 《Linux命令行与shell脚本编程大全》 第二十二章 学习笔记
第二十二章:使用其他shell 什么是dash shell Debian的dash shell是ash shell的直系后代,ash shell是Unix系统上原来地Bourne shell的简化版本 ...
- “全栈2019”Java多线程第三十二章:显式锁Lock等待唤醒机制详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- [CSAPP笔记][第十二章并发编程]
第十二章 并发编程 如果逻辑控制流在时间上是重叠,那么它们就是并发的(concurrent).这种常见的现象称为并发(concurrency). 硬件异常处理程序,进程和Unix信号处理程序都是大家熟 ...
随机推荐
- hdu3511-Prison Break
纪念一下人生中第一道扫描线算法的题.....其实不是严格上的第一道...第一次遇到的那个至今没过..... 题目链接: http://acm.hdu.edu.cn/showproblem.php?pi ...
- layer的基本使用
layer是layui独立出来的一个弹出层模块 <!DOCTYPE html> <html> <head> <meta charset="UTF-8 ...
- 5.29MyBatis Generator
二.MyBatis Generator CRUD(创建,检索,更新,删除) 与表结构匹配的Java POJO BLOB (binary large object),二进制大对象,是一个可以存储二进制文 ...
- AFN上传多张图片
AFN上传多张图片代码: AFHTTPSessionManager *sessionManager = [AFHTTPSessionManager manager]; sessionManager.r ...
- Android 新浪微博开放平台应用 android签名怎么获得
方法一: 通过命令行,直接生成MD5值 keytool -list -v -keystore keystorefile -storepass 其中keytool为jdk自带工具:keystorefil ...
- 基于CXF搭建webService
1.导入相关jar包,具体哪些包我记不太清了 2.在applicationContext中加入相关配置信息,如下所示: <beans xmlns="http://www.springf ...
- js match 来点击切换图片
定义和用法 match() 方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配. 该方法类似 indexOf() 和 lastIndexOf(),但是它返回指定的值,而不是字符串的位置. ...
- 关于angular双向绑定的一个问题,百度无果,还请帮忙解惑。
用了一段时间anjular蛮好用的.其实用的功能不多.主要用于列表数据绑定以及一些简单效果的绑定,但是最近出现一个现象,百度无果,居然没有人遇到.现在描述一下,截图不方便,希望有人解惑. 列表ng-r ...
- 2、scala条件控制与循环
1. if表达式 2. 句终结符.块表达式 3. 输入与输出 4. 循环 5. 高级for循环 1. if表达式 if表达式的定义:scala中,表达式是有值的,就是if或者else中最后 ...
- PythonOpenCV:MLP用于最近邻搜索
一:简单C++版本的链接: http://blog.csdn.net/kaka20080622/article/details/9039749 OpenCV的ml模块实现了人工神经网络(Artific ...