多线程的创建,pthread_create:
1 |
//头文件 |
2 |
#include<pthread.h> |
3 |
//函数声明 |
4 |
int pthread_create( /*指向线程标识符的指针*/ , /*线程属性参数,通常为NULL*/ , /*返回值是void类型指针的函数 */ , /*运行函数的参数*/ ); |
5 |
//成功返回0,失败返回错误编号。 |
注意:被创建的线程可能在pthread_create执行完毕之前就开始执行。
编译注意:编译时注意加上-lpthread参数,以调用静态链接库。因为pthread并非Linux系统的默认库。
等待线程结束,pthread_join:
1 |
pthread_join( /*等待的线程标识符*/ , /*传入指针,用于存储被等待线程的返回值,通常为NULL*/ ); |
一开始以为这个函数没啥用,所以第一次写多线程没加入这个函数,结果发现用线程执行的函数没执行程序就结束了。才明白这个函数是让主线程阻塞在这个地方等待子线程结束。
如果主线程执行完,return 0,子线程都会直接结束掉,因为系统直接回收资源了。
一个简单的栗子:
01 |
#include<iostream> |
02 |
#include<cstdio> |
03 |
#include<string> |
04 |
05 |
#include<fcntl.h> |
06 |
#include<errno.h> //错误代码 |
07 |
#include<stdlib.h> //exit函数 |
08 |
#include <pthread.h> //线程库 |
09 |
10 |
using namespace std; |
11 |
12 |
pthread_cond_t cond; |
13 |
pthread_mutex_t mutex; |
14 |
int count=0; |
15 |
void *play( void *arg) //注意这个函数,返回值和参数都是void指针。 |
16 |
{ |
17 |
cout<< "child thread" <<endl; |
18 |
} |
19 |
int main() |
20 |
{ |
21 |
pthread_t pid; |
22 |
cout<< "main thread" <<endl; |
23 |
pthread_create(&pid,NULL,play,NULL); //创建线程 |
24 |
pthread_join(pid,NULL); //等待线程结束 |
25 |
return 0; |
26 |
} |
至此,简单的多线程可以写了。
运行函数的参数传递问题:
就是pthread_create的最后一个参数。这是给运行的函数传递参数用的。
没有参数:
多好,直接给个NULL就完了。
一个参数:
可以在样例代码中看到,传入的函数的参数是一个void指针,所以pthread_create的第四个参数接受的也是一个void指针。对变量取个地址即可传入,然后在函数中重新转换回原本变量的指针类型,再解引用即可。(这里需要注意的是传入地址,线程中对变量的改变会引起原变量的改变,如果指针指向一块动态内存,一旦内存被回收,也会出现问题)
多个参数:
毕竟,一个函数传入多个参数是很常见的。但是运行函数的参数只有一个void指针,所以要传入多个参数就必须想办法把它们“打包”通过那个void指针传递进去。
于是,第一种方法就是采用一个结构体,对多个变量打包传入。像单个参数一样指针转换一下,提取出变量即可。
第二种方法,使用void数组。创建一个void*[],里面保存指向你的所有参数的指针,然后把这个void*[]传递给pthread_create。
01 |
void* route(void* args) |
02 |
{ |
03 |
int* iptr = ((void**)args)[0]; //注意这里,是void**,而不是void* |
04 |
float* fptr = ((void**)args)[1]; |
05 |
char* str = ((void**)args)[2]; |
06 |
printf("integer: %d/nfloat: %f/nstring: %s/n", *iptr, *fptr, str); |
07 |
return 0; |
08 |
} |
09 |
|
10 |
int main(void) |
11 |
{ |
12 |
pthread_t thr_id; |
13 |
int ival = 1; |
14 |
float fval = 10.0F; |
15 |
char buf[] = "func"; |
16 |
void* arg[3]={&ival, &fval, buf}; //void指针数组 |
17 |
pthread_create(&thr_id, NULL, route, arg); |
18 |
sleep(1); |
19 |
} |
和第一种方法的不同之处在于args是一个void指针,但是我们要把它当作二级指针用。void指针是可以指向任何地址的,args其实是指向那个arg[]数组的,我们知道数组其实就是一个指针,所以args是指针的指针。
但是void指针是不能解引用的,它不代表真实的变量,因为真实的变量还有变量大小,对应的指针能根据变量大小来加减,void*是没有的,也就不能做算法操作。
所以代码中为了支持下标操作,把void*转换成void**。
不是说了void*是没有类型的么,为啥void**能支持下标操作?
void* 是说: 这是一个指针,它指向的对象未知.
void* 是说: 这是一个指针,它指向一个void* 型的数据结构.void*是一个指针,指针长度是已知的,所以也就可以支持下标操作了。
总结:
总算是能写个多线程的程序了,但是这个离会多线程还差得远,因为多线程的难度并不在于创建线程,而是在于线程的同步。
在linux中线程其实并没有专门独立开来说,实际上线程是使用轻量级进程实现的。所以linux是每个进程只有一个线程。
pthread_create和fork的区别:
fork出来的新进程和父进程是处于一种copy。是一个新的副本。但是pthread_create创建的线程却能共享变量。
转载请注明:旅途@KryptosX » 初探linux pthread多线程编程