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信号处理程序都是大家熟 ...
随机推荐
- PHP无限级分类实现(递归+非递归)
<?php /** * Created by PhpStorm. * User: qishou * Date: 15-8-2 * Time: 上午12:00 */ //准备数组,代替从数据库中检 ...
- 【LuoguP5004】 专心OI - 跳房子
首先这是一道计数类DP,那我们得先推式子,经过瞎掰乱凑,经过认真分析,我们可以得到这样的方程 F(N)=F(0)+F(1)+....+F(N-M-1) 所有F初值为1,F(1)=2 ANS=F(N+M ...
- django-2的路由层(URLconf)
URL配置(URLconf)就像Django 所支撑网站的目录.它的本质是URL与要为该URL调用的视图函数之间的映射表:你就是以这种方式告诉Django,对于客户端发来的某个URL调用哪一段逻辑代码 ...
- 【WPF】使用 XAML 的 Trigger 系统实现三态按钮
利用 WPF 的 Trigger 系统,也可以很简单的只使用xmal实现三态按钮.在Window或UserControl的资源中声明按钮的style并加入触发功能.使用的时候直接在button里复写s ...
- 设计模式之桥接模式(Java语言描述)
桥接模式定义 將抽象部分与它的具体实现部分分离,使它们都可以独立地变化.它是一种对象结构型模式,又称为柄体模式或接口模式. Decouple an abstraction from its imple ...
- HEK_费用报表审核无审核权限,有些字段无法编辑的问题处理
Q:HEK_费用报表审核无审核权限,有些字段无法编辑的问题处理 A:设置AP员工->给AP员工分配审批权限->绑定员工和ERP账号 1.将审核人设置为AP员工 2.分配给员工审批权限 3. ...
- jqurey事件 ready方法用法
ready 在文档加载后激活函数 例: <html> <head> <script type="text/javascript" src=" ...
- 复习java基础第四天(集合:List、Map、Collections、Enumeration)
一.List: List 代表一个元素有序.且可重复的集合,集合中的每个元素都有其对应的顺序索引 List 允许使用重复元素,可以通过索引来访问指定位置的集合元素. List 默认按元素的添加顺序设置 ...
- 如何在编辑器打开Java程序
我们都知道运行JAVA文件,可以从软件控制台运行我们写好的java文件,也可以从windows窗口运行,我们最开始接触的是通过windows窗口来运行java文件,下面简单介绍一下如何如何在编辑器打开 ...
- Spring Boot 整合mybatis时遇到的mapper接口不能注入的问题
现实情况是这样的,因为在练习spring boot整合mybatis,所以自己新建了个项目做测试,可是在idea里面mapper接口注入报错,后来百度查询了下,把idea的注入等级设置为了warnin ...