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. Anagram Groups(字符串)

    http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&problemid=2316 理解错一点题意就能WA到死...题中对于 ...

  2. Help Me with the Game(模拟)

    http://poj.org/problem?id=2996 #include<stdio.h> #include<string.h> ][]; void find1(char ...

  3. log4net实用配置代码

    log4net实用配置代码 <?xml version="1.0" encoding="utf-8" ?> <configuration> ...

  4. angular的directive指令的link方法

    比如 指令标签 <mylink myLoad="try()"></mylink> link:function(scope,element,attr){ el ...

  5. Windows7环境下Composer 安装包的Cache目录位置

    http://segmentfault.com/a/1190000000355928 https://getcomposer.org/doc/ 要说Composer的用法,以后再说,现在只记录wind ...

  6. 深入浅出java多态

    所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个 ...

  7. 本地Gradle配置方法,免去长时间的更新同步等待

    通常gradle项目在gradle\wrapper\gradle-wrapper.properties中配置在线gradle: distributionBase=GRADLE_USER_HOME di ...

  8. Android开发笔记(13)——ListFragment

    转载请注明:http://www.cnblogs.com/igoslly/p/6959108.html ListFragment ListFragment是继承于Fragment的类,专门用于包含Li ...

  9. 【PostgreSQL-9.6.3】创建、修改、删除数据库

    1.创建数据库 create database database_name; 2.修改数据库的名称 alter database database_name rename to new_databas ...

  10. VR: AR和VR演进哲学

    Facebook 20亿美元(4亿美元+16亿美元股票换购方式)收购虚拟现实厂商Oculus 引爆AR产业,索尼不温不火逐步演进的头盔项目也该加速了.最近Oculus rift发布了商业版本:Ocul ...