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. z-index 、层叠上下文、层叠级别、z-index失效

    一.z-index z-index默认处于非激活状态,只有定位元素(即position:relative/absolute/fixed时)才会被激活. z-index与层叠上下文关联. 当z-inde ...

  2. Largest Rectangle in a Histogram(dp)

    http://acm.hdu.edu.cn/showproblem.php?pid=1506 题意:给出n个矩形的高度,每个矩形的宽都为1,求相邻的矩形能组合成的最大的矩形的面积. 思路:求出比第i个 ...

  3. gitlab克隆报错:remote: HTTP Basic: Access denied;remote: You must use a personal access token with ‘api’ scope for Git over HTTP.

    错误: remote: HTTP Basic: Access denied remote: You must use a personal access token with ‘api’ scope ...

  4. Android开发中的日期格式化

    下面的转换符来自Java,但是在android中同样可用.(以下表格内容来自互联网.) 常见日期格式化转换符 转换符 说 明 示 例 %te  一个月中的某一天(1-31)  2 %tb  指定语言环 ...

  5. EmguCV学习——视频与图片互转

    其实视频转图片在上篇文章中已经有些眉目了,其实就是按帧读取视频,然后把帧保存就ok.然后自己再加个进度条美化一下...这代码简单易懂,还是直接上代码吧. 视频转图片 /// <summary&g ...

  6. jQuery 滑动及点击切换效果

    效果图如下: 初始化 hover效果:滑动menuitem,‘首页’不变,字体颜色改变,有下划线展示. 即在动态添加boder-bottom,改变字体颜色颜色 .menuItem:hover{ bor ...

  7. Linux scp 后台运行传输文件

    Linux scp 设置nohup后台运行 1.正常执行scp命令 2.输入ctrl + z 暂停任务 3.bg将其放入后台 4.disown -h 将这个作业忽略HUP信号 5.测试会话中断,任务继 ...

  8. MSP430之data type

  9. 2星|《工业X.0》:物联网的资料汇编

    工业X.0:实现工业领域数字价值 看完比较失望,没有看到新的观点想法.基本算是物联网的资料汇编.总体评价2星. 以下是书中一些内容的摘抄: 1:例如,埃森哲为其员工开发了一个用例,用增强现实技术解决实 ...

  10. Linux命令小记

    以下说法都是基于普通用户的角度,如果是root,可能会有不同. (1)rm -r或-R选项:递归删除目录及其内容(子目录.文件) rm默认无法删除目录,如果删除空目录,可以使用-d选项.如果目录非空, ...