C++ 多线程编程
多线程是多任务处理的一种特殊形式,多任务处理允许让电脑同时运行两个或两个以上的程序
有两种类型的多任务处理 基于进程 和 基于线程
-
基于进程的多任务处理是程序的并发执行
Windows 下 QQ 可以多开就是多进程处理的一个例子
-
基于线程的多任务处理是同一程序的片段的并发执行
迅雷下载就是一个多线程任务的例子
多线程 程序包含可以同时运行的两个或多个部分,这样的程序中的每个部分称为一个线程,每个线程定义了一个单独的执行路径
本章节是基于 POSIX
操作系统的,因为我们要用它的 <pthread.h>
和 libpthread.a
来开发多线程程序
POSIX 表示可移植操作系统接口 ( Portable Operating System Interface of UNIX,缩写为 POSIX )
目前主流的 Linux
和 Mac OS
都属于 POSIX
系统,但是,抱歉,Windows
不是
所以,如果你使用的是 Windows
电脑,别灰心,先练练其它的,以后有的是机会学习这个
pthread
-
基于
POSIX
开发多线程程序需要加载<pthread.h>
#include <pthread.h>
-
pthread
提供了一个类型pthread_t
用来表示一个线程 -
pthread
库不是Linux
和Mac OS
系统默认的库,编译连接时需要加上静态库libpthread.a
,就像下面这样g++ main.cpp -lpthread
创建线程 pthread_create()
pthread.h
提供了 pthread_create()
用来创建一个 POSIX 线程,并立即让它执行
pthread_create (thread, attr, start_routine, arg)
创建线程成功时,函数返回 0,若返回值不为 0 则说明创建线程失败
参数 | 描述 |
---|---|
thread | 指向线程标识符指针 |
attr | 一个不透明的属性对象,用来设置线程属性 可以指定线程属性对象,也可以传递 NULL |
start_routine | 线程运行函数起始地址,一旦线程被创建就会执行 |
arg | 运行函数的参数 它必须通过把引用作为指针强制转换为 void 类型进行传递 如果没有传递参数,可以使用 NULL |
我们写一个范例来演示下 pthread_create()
/** * file: main.cpp * author: 简单教程(www.twle.cn) * * Copyright © 2015-2065 www.twle.cn. All rights reserved. */ #include <iostream> #include <pthread.h> // 线程要运行的函数 void* greeting(void* args) { std::cout << "Hello www.twle.cn" << std::endl; return NULL; } int main() { // 定义线程的 id 变量 pthread_t tid; // 创建线程 // 参数依次是:创建的线程id,线程参数,调用的函数,传入的函数参数 int ret = pthread_create(&tid, NULL, greeting, NULL); // 创建失败 if (ret != 0) { std::cout << "pthread_create error: error_code = "; std::cout << ret << std::endl; } return 0; }
编译运行上面的 C++ 代码,输出结果如下
$ g++ main.cpp -lpthread && a.out main.cpp:18:1: warning: control reaches end of non-void function [-Wreturn-type] } ^ 1 warning generated.
发生了什么事?怎么什么都没有输出?
别着急啊,原因是这样的,就是呢,main()
函数虽然创建了一个线程,但是它没有等它运行完可能自己就先执行完了,所以就没有任何输出了
要等待新创建的线程运行完,main()
再退出,就要用到 pthread
提供的另一个函数 pthread_exit()
终止线程 pthread_exit()
pthread_exit()
函数用来终止一个 POSIX 线程,其实它的作用更像是向创建它的人说我要退出啦...
pthread_exit (status)
pthread_exit()
用于显式地退出一个线程,通常情况下,pthread_exit()
函数是在线程完成工作后无需继续存在时被调用
线程执行完任务了也会自己退出,不用显示调用 pthread_exit()
,不然有点像脱裤子放屁,多此一举
一般情况下,pthread_create()
创建的线程会在 main()
函数执行完毕时自动终止
对,这就是上面范例没有任何输出的原因
但如果在 main()
函数中显示的调用了 pthread_exit()
,那么主线程(可以理解为 main()
函数) 就会继续等待它创建的线程全部退出,自己再退出
我们把上面那个范例完善下,让 main()
调用 pthread_exit()
等待子线程退出
/** * file: main.cpp * author: 简单教程(www.twle.cn) * * Copyright © 2015-2065 www.twle.cn. All rights reserved. */ #include <iostream> #include <pthread.h> // 线程要运行的函数 void* greeting(void* args) { std::cout << "Hello www.twle.cn" << std::endl; return NULL; } int main() { // 定义线程的 id 变量 pthread_t tid; // 创建线程 // 参数依次是:创建的线程id,线程参数,调用的函数,传入的函数参数 int ret = pthread_create(&tid, NULL, greeting, NULL); // 创建失败 if (ret != 0) { std::cout << "pthread_create error: error_code = "; std::cout << ret << std::endl; } pthread_exit( NULL ); return 0; }
编译运行上面的 C++ 代码,输出结果如下
$ g++ main.cpp -lpthread && a.out
Hello www.twle.cn
唠叨几句,我一开始很不能理解为什么加一个 pthread_exit(NULL)
就会等待子线程退出,后来我想了想,其实这个的意思就是告诉 main()
的调用者,我要退出了,可以吗? 调用者一看,你还有子线程没运行完呢,你再等等
创建一打线程
我们已经学会了如何创建单个线程,如果要创建多个呢?比如创建一打 (12个),要怎么做呢?
别紧张,都一样,区别只是要创建一个数组来装这一打线程的 id 值
/** * file: main.cpp * author: 简单教程(www.twle.cn) * * Copyright © 2015-2065 www.twle.cn. All rights reserved. */ #include <iostream> #include <pthread.h> // 线程要运行的函数 void* greeting(void* args) { std::cout << "Hello www.twle.cn\n"; return NULL; } // 定义线程数量 #define NUM_THREADS 12 int main() { // 定义线程的 id 变量,多个变量使用数组 pthread_t tids[NUM_THREADS]; for(int i = 0; i < NUM_THREADS; ++i) { int ret = pthread_create(&tids[i], NULL, greeting, NULL); if (ret != 0) { std::cout << "pthread_create error: error_code="; std::cout << ret << std::endl; } } pthread_exit(NULL); return 0; }
编译运行上面的 C++ 代码,输出结果如下
$ g++ main.cpp -lpthread && a.out
Hello www.twle.cn
Hello www.twle.cn
Hello www.twle.cn
Hello www.twle.cn
Hello www.twle.cn
Hello www.twle.cn
Hello www.twle.cn
Hello www.twle.cn
Hello www.twle.cn
Hello www.twle.cn
Hello www.twle.cn
Hello www.twle.cn
pthread_create() 中的 arg 参数
上面的范例一口气创建了 12 个线程输出了 12 个 Hello www.twle.cn
,但是,鬼知道哪个是哪个输出的啊,有没有办法标记下呢?
有的,就是 pthread_create()
中最后那个 arg
参数
arg
参数是主动传递给运行函数 (start_routine) 的参数,它必须强制转换为 void 类型进行传递
有了这个参数,我们就可以把当前线程的顺序传递给 greeting()
函数了
/** * file: main.cpp * author: 简单教程(www.twle.cn) * * Copyright © 2015-2065 www.twle.cn. All rights reserved. */ #include <stdio.h> #include <pthread.h> // 线程要运行的函数 void* greeting(void* args) { // 对传入的参数进行强制类型转换 // 由无类型指针变为整形数指针,然后再读取 int tid = *((int*)args); printf("Thread %2d say: Hello www.twle.cn\n",tid); return NULL; } // 定义线程数量 #define NUM_THREADS 12 int main() { // 定义线程的 id 变量,多个变量使用数组 pthread_t tids[NUM_THREADS]; // 创建一个 index 数组用于保存线程顺序 int index[NUM_THREADS]; for(int i = 0; i < NUM_THREADS; ++i) { index[i] = i+1; int ret = pthread_create(&tids[i], NULL, greeting, (void *)&(index[i])); if (ret != 0) { printf("pthread_create error: error_code=%d\n",ret); } } pthread_exit(NULL); return 0; }
为什么抛弃了 <iostream>
用 <stdio>
,那是因为好理解,不然你可以试一试,试完了就会问我一大堆的问题了
编译运行上面的 C++ 代码,输出结果如下
$ g++ main.cpp -lpthread && a.out Thread 1 say: Hello www.twle.cn Thread 3 say: Hello www.twle.cn Thread 2 say: Hello www.twle.cn Thread 4 say: Hello www.twle.cn Thread 5 say: Hello www.twle.cn Thread 6 say: Hello www.twle.cn Thread 7 say: Hello www.twle.cn Thread 8 say: Hello www.twle.cn Thread 9 say: Hello www.twle.cn Thread 10 say: Hello www.twle.cn Thread 11 say: Hello www.twle.cn Thread 12 say: Hello www.twle.cn
向线程传递参数
终于看清了哪个是哪个输出的了,但是前篇一律的 Hello www.twle.cn
也是烦人啊,这个能否也作为参数传递呢?
答案是肯定的,我们可以构造一个结构体,然后通过传递结构体的方法达到传递多个参数
/** * file: main.cpp * author: 简单教程(www.twle.cn) * * Copyright © 2015-2065 www.twle.cn. All rights reserved. */ #include <stdio.h> #include <pthread.h> // 定义一个结构体作为参数 struct thread_data{ int tid; const char *msg; }; // 线程要运行的函数 void* greeting(void* args) { struct thread_data *data; data = (struct thread_data *) args; printf("Thread %2d say: Hello %s\n",data->tid,data->msg); return NULL; } // 定义线程数量 #define NUM_THREADS 5 // 定义 5 个问候对象 const char *sayto[5] = { "Python", "PHP", "Perl", "www.twle.cn", "C++" }; int main() { // 定义线程的 id 变量,多个变量使用数组 pthread_t tids[NUM_THREADS]; struct thread_data td[NUM_THREADS]; for(int i = 0; i < NUM_THREADS; ++i) { td[i].tid = i+1; td[i].msg = sayto[i]; int ret = pthread_create(&tids[i], NULL, greeting, (void *)&(td[i])); if (ret != 0) { printf("pthread_create error: error_code=%d\n",ret); } } pthread_exit(NULL); return 0; }
编译运行上面的 C++ 代码,输出结果如下
$ g++ main.cpp -lpthread && a.out Thread 2 say: Hello PHP Thread 3 say: Hello Perl Thread 1 say: Hello Python Thread 4 say: Hello www.twle.cn Thread 5 say: Hello C++