c++ 多线程(c++11标准以前) 基础 C++标准并没有提供对多进程并发的原生支持,所以C++的多进程并发要靠其他API——这需要依赖相关平台。
Linux系统下的多线程遵循POSIX线程接口,称为pthread。编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a。顺便说一下,Linux下pthread的实现是通过系统调用clone()来实现的。clone()是Linux所特有的系统调用,它的使用方式类似fork。
POSIX:
可移植操作系统接口 (英语:Portable Operating System Interface,缩写为POSIX )是IEEE 为要在各种UNIX 操作系统 上运行软件,而定义API 的一系列互相关联的标准的总称。
C++11 标准提供了一个新的线程库,内容包括了管理线程、保护共享数据、线程间的同步操作、低级原子操作等各种类。标准极大地提高了程序的可移植性,以前的多线程依赖于具体的平台,而现在有了统一的接口进行实现。
C++11 新标准中引入了几个头文件来支持多线程编程:
< thread > :包含std::thread类以及std::this_thread命名空间。
< atomic > :包含std::atomic和std::atomic_flag类,以及一套C风格的原子类型和与C兼容的原子操作的函数。
< mutex > :包含了与互斥量相关的类以及其他类型和函数。
< future > :包含两个Provider类(std::promise和std::package_task)和两个Future类(std::future和std::shared_future)以及相关的类型和函数。
< condition_variable > :包含与条件变量相关的类,包括std::condition_variable和std::condition_variable_any。
创建线程 1 2 #include <pthread.h> pthread_create (thread, attr, start_routine, arg) ;
pthread_create的四个参数
thread :指向线程标识符指针。
attr :一个不透明的属性对象,可以被用来设置线程属性。您可以指定线程属性对象,也可以使用默认值 NULL。
start_routine :线程运行函数起始地址,一旦线程被创建就会执行。
arg :运行函数的参数。它必须通过把引用作为指针强制转换为 void 类型进行传递。如果没有传递参数,则使用 NULL。
创建线程成功时,函数返回 0,若返回值不为 0 则说明创建线程失败。
终止线程 1 2 #include <pthread.h> pthread_exit (status)
在这里,pthread_exit 用于显式地退出一个线程。通常情况下,pthread_exit() 函数是在线程完成工作后无需继续存在时被调用。线程在运行完成后会自动隐式地退出。
例子1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include <iostream> #include <cstdlib> #include <pthread.h> using namespace std ; #define NUM_THREADS 5 void *PrintHello (void *threadid) { int tid = *((int *)threadid); cout << "Hello Runoob! 线程 ID, " << tid << endl ; pthread_exit(NULL ); } int main () { pthread_t threads[NUM_THREADS]; int indexes[NUM_THREADS]; int rc; int i; for ( i=0 ; i < NUM_THREADS; i++ ){ cout << "main() : 创建线程, " << i << endl ; indexes[i] = i; rc = pthread_create(&threads[i], NULL , PrintHello, (void *)&(indexes[i])); if (rc){ cout << "Error:无法创建线程," << rc << endl ; exit (-1 ); } } pthread_exit(NULL ); }
编译并运行后预期产生如下结果:
1 2 3 4 5 6 7 8 9 10 11 12 $ g++ test.cpp -lpthread -o test.o $ ./test.o main() : 创建线程, 0 main() : 创建线程, 1 Hello Runoob! 线程 ID, 0 main() : 创建线程, Hello Runoob! 线程 ID, 21 main() : 创建线程, 3 Hello Runoob! 线程 ID, 2 main() : 创建线程, 4 Hello Runoob! 线程 ID, 3 Hello Runoob! 线程 ID, 4
实际在Ubuntu16中测试结果却是:
1 2 3 4 5 6 7 8 9 10 main() : 创建线程, 0 main() : 创建线程, 1 main() : 创建线程, 2 main() : 创建线程, 3 main() : 创建线程, 4 Hello Runoob! 线程 ID, 4 Hello Runoob! 线程 ID, 3 Hello Runoob! 线程 ID, 2 Hello Runoob! 线程 ID, 1 Hello Runoob! 线程 ID, 0
非常规律,目前原因未知。
例子2(包含pthread_join的使用) pthread_join()函数原型:
int pthread_join(pthread_t thread, void **retval);
两个参数:
thread: 线程标识符,即线程ID,标识唯一线程。
retval: 用户定义的指针,用来存储被等待线程的返回值。
返回值: 线程连接的状态,0是成功,非0是失败
linux线程执行和windows不同,pthread有两种状态joinable状态和unjoinable状态,如果线程是joinable状态,当线程函数自己返回退出时或pthread_exit时都不会释放线程所占用堆栈和线程描述符(总计8K多)。只有当你调用了pthread_join之后这些资源才会被释放。若是unjoinable状态的线程,这些资源在线程函数退出时或pthread_exit时自动会被释放。
unjoinable属性可以在pthread_create时指定,或在线程创建后在线程中pthread_detach自己, 如:pthread_detach(pthread_self()),将状态改为unjoinable状态,确保资源的释放。或者将线程置为 joinable,然后适时调用pthread_join。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 #include <iostream> #include <cstdlib> #include <pthread.h> #include <unistd.h>//包含unix系统中的各种原语函数,如sleep、read、write、fork,unistd.h在unix中类似于window中的windows.h using namespace std ; #define NUM_THREADS 5 void *wait (void *t) { int i; long tid; tid = (long )t; sleep(1 ); cout << "Sleeping in thread " << endl ; cout << "Thread with id : " << tid << " ...exiting " << endl ; pthread_exit(NULL ); } int main () { int rc; int i; pthread_t threads[NUM_THREADS]; pthread_attr_t attr; void *status; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); for ( i=0 ; i < NUM_THREADS; i++ ){ cout << "main() : creating thread, " << i << endl ; rc = pthread_create(&threads[i], NULL , wait, (void *)&i ); if (rc){ cout << "Error:unable to create thread," << rc << endl ; exit (-1 ); } } pthread_attr_destroy(&attr); for ( i=0 ; i < NUM_THREADS; i++ ){ rc = pthread_join(threads[i], &status); if (rc){ cout << "Error:unable to join," << rc << endl ; exit (-1 ); } cout << "Main: completed thread id :" << i ; cout << " exiting with status :" << status << endl ; } cout << "Main: program exiting." << endl ; pthread_exit(NULL ); }
在ubuntu16编译后结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 main() : creating thread, 0 main() : creating thread, 1 main() : creating thread, 2 main() : creating thread, 3 main() : creating thread, 4 Sleeping in thread Thread with id : 140724715288192 ...exiting Sleeping in thread Thread with id : 140724715288192 ...exiting Sleeping in thread Thread with id : 140724715288192 ...exiting Sleeping in thread Thread with id : 140724715288192 ...exiting Sleeping in thread Thread with id : 140724715288192 ...exiting Main: completed thread id :0 exiting with status :0 Main: completed thread id :1 exiting with status :0 Main: completed thread id :2 exiting with status :0 Main: completed thread id :3 exiting with status :0 Main: completed thread id :4 exiting with status :0 Main: program exiting.
这里有一点很困惑我,把tid=(long)t;
改成tid=*((long*)t)
后,每个线程的id显示都是0。
显示如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 main() : creating thread, 0 main() : creating thread, 1 main() : creating thread, 2 main() : creating thread, 3 main() : creating thread, 4 Sleeping in thread Thread with id : 0 ...exiting Sleeping in thread Thread with id : 0 ...exiting Sleeping in thread Thread with id : 0 ...exiting Sleeping in thread Thread with id : 0 ...exiting Sleeping in thread Thread with id : 0 ...exiting Main: completed thread id :0 exiting with status :0 Main: completed thread id :1 exiting with status :0 Main: completed thread id :2 exiting with status :0 Main: completed thread id :3 exiting with status :0 Main: completed thread id :4 exiting with status :0 Main: program exiting.
不论是储存线程创建时的值,还是公用同一片int空间,都不应该是0啊,如果是公用一个int的空间那在for循环结束后i的内存空间的值也应该是5。这里目前还弄不明白。
参考 https://www.cnblogs.com/shuqingstudy/p/9747004.html C++中的并发与多线程
https://www.runoob.com/cplusplus/cpp-multithreading.html 菜鸟课程C++多线程
https://baike.baidu.com/item/%E5%8F%AF%E7%A7%BB%E6%A4%8D%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E6%8E%A5%E5%8F%A3/12718298?fr=aladdin&fromtitle=POSIX&fromid=3792413 百度百科可移植操作系统接口
https://www.cnblogs.com/x_wukong/p/5671137.html linux的<pthread.h>
https://blog.csdn.net/liuzhanchen1987/article/details/8009701 linux标准库#include
https://www.cnblogs.com/leijiangtao/p/3995826.html 线程属性的初始化以及销毁
https://blog.csdn.net/weibo1230123/article/details/81410241 linux中pthread_join()与pthread_detach()详解
评论加载中