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程序设计——多线程(第十二章)的更多相关文章

  1. “全栈2019”Java多线程第二十二章:饥饿线程(Starvation)详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  2. “全栈2019”Java多线程第十二章:后台线程setDaemon()方法详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  3. 鸟哥的linux私房菜——第十二章学习(Shell Scripts)

    第十二章  Shell Scripts 1.0).什么是shell scripts? script 是"脚本.剧本"的意思.整句话是说, shell script 是针对 shel ...

  4. 鸟哥的Linux私房菜——第十二章:档案的压缩与打包

    视频链接: 土豆:http://www.tudou.com/programs/view/GncwT0FJKsQ B站(推荐):http://www.bilibili.com/video/av98857 ...

  5. JavaScript高级程序设计:第十二章

    DOM1级主要定义的是HTML和XML文档的底层结构.DOM2和DOM3级则在这个结构的基础上引入了更多的交互能力,也支持了更高级的XML特性.为此DOM2和DOM3级分为许多模块,这些模块如下: D ...

  6. linux高级管理第十二章--rsync

    实验部分 1.安装rsync 2.配置文件 3.配置密码 4.后续 5.为了测试,创建几个文件 配置实时同步 1.调整inotify内核参数 安装inotify-tools 测试同步 编写脚本 验证 ...

  7. Linux学习笔记(第十二章)

    grep进阶 grep:以整行为单位进行截取 截取的特殊符号 正规表示法特殊字符 注意: sed用法 格式化打印 awk 用法 diff档案对比: path旧文档升级为新文档

  8. 《Linux命令行与shell脚本编程大全》 第二十二章 学习笔记

    第二十二章:使用其他shell 什么是dash shell Debian的dash shell是ash shell的直系后代,ash shell是Unix系统上原来地Bourne shell的简化版本 ...

  9. “全栈2019”Java多线程第三十二章:显式锁Lock等待唤醒机制详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  10. [CSAPP笔记][第十二章并发编程]

    第十二章 并发编程 如果逻辑控制流在时间上是重叠,那么它们就是并发的(concurrent).这种常见的现象称为并发(concurrency). 硬件异常处理程序,进程和Unix信号处理程序都是大家熟 ...

随机推荐

  1. CSS--浏览器CSS Hack 收集

    所谓的Hack就是只有特定浏览器才能识别这段hack代码.Hack 不是什么好东西,除非没有办法,我们尽量还是不要用着玩意. 下面是各个浏览器的CSS Hack 列表. Firefox 浏览器 @-m ...

  2. JDBC-ODBC桥接器连接Access数据库

    今天,遇到一个问题,虽然不是什么大难题,但对于初学者来说也缠绕了我好久!(好气哦) 问题: 运行jsp项目连接不上数据库: java.sql.SQLException: [Microsoft][ODB ...

  3. python 4:str.lstrip()、str.rstrip()、str.strip()(分别去除首空格,尾空格,首尾空格;不改变原有变量,除非赋给)

    name = " Hello,World! Hello,Python! " print(name + "检测行末空格的") print(name.lstrip( ...

  4. [2月1号] 努比亚全机型ROM贴 最全最新NubiaUI5.0 ROOT 极速体验

    前言 感谢在开发过程中mandfx和dgtl198312予以的帮助!本帖将整理所有Nubia手机的最新刷机包,还有少数机型未制作刷机包,需要的机油可以联系我制作recovery以及刷机包加群23722 ...

  5. Spark on Yarn集群搭建

    软件环境: linux系统: CentOS6.7 Hadoop版本: 2.6.5 zookeeper版本: 3.4.8 主机配置: 一共m1, m2, m3这五部机, 每部主机的用户名都为centos ...

  6. SqlServer 导出指定表数据 生成Insert脚本

    版权声明:本文为博主原创文章,未经博主允许不得转载.

  7. 时序分析:DTW算法(基于模板)

    对时序对象进行分析,使用KMP算法可以分析速率不变的模式,参考时序分析:欧式空间轨迹模式识别.使用基于模板匹配的方法,对于速率发生变化的模式,需要用新的对速率要求松散的方法,DTW方法为一种广泛使用的 ...

  8. react基础篇五

    再看JSX 本质上来讲,JSX 只是为 React.createElement(component, props, ...children) 方法提供的语法糖.比如下面的代码: <MyButto ...

  9. Repeater + 分页控件 AspNetPager 研究

    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default3.aspx.cs ...

  10. 虚拟DOM介绍

    [转自]:https://www.jianshu.com/p/616999666920 为什么需要虚拟DOM 先介绍浏览器加载一个HTML文件需要做哪些事,帮助我们理解为什么我们需要虚拟DOM.web ...