线程是多任务处理的一种特殊形式,多任务处理允许让电脑同时运行两个或两个以上的程序。一般情况下,两种类型的多任务处理:基于进程和基于线程。
- 基于进程的多任务处理是程序的并发执行。
- 基于线程的多任务处理是同一程序的片段的并发执行。
pthread_t 定义线程变量。
pthread_create(thread, attr, start_routine, arg);
创建新的线程,并让其执行,参数说明:
thread : 指向线程嗯标识符指针。
attr:不透明的属性对象,可以被用来设置线程属性。你可以制定线程属性对象爱嗯,也可以使用默认值NULL。
star‘t_routine:线程运行函数的起始地址,一旦线程被创建就用执行。
arg :运行函数的参数,它必须通过把引用作为指针强制转换为void类型进行传递,如果没有传递参数,则使用NULL。
函数创建成功返回值为0,否则为失败。(凭借此可以判断线程是否创建成功)
线程终止函数:pthread_exit(status) ;//可以终止一个posix线程
该函数用于显式的退出一个线程,一般使用情况是,线程完成工作后无需继续存在时被调用。
tips:
如果main()是再它创建的线程之前结束,并通过pthread_exit()退出,那么其他线程将继续执行,否则,他们会再main()结束时被自动终止。
#include <iostream>
// 必须的头文件
#include <pthread.h>
using namespace std;
#define NUM_THREADS 5
// 线程的运行函数
void* say_hello(void* args)
{
cout << "Hello Runoob!" << endl;
return 0;
}
int main()
{
// 定义线程的 id 变量,多个变量使用数组
pthread_t tids[NUM_THREADS];
for(int i = 0; i < NUM_THREADS; ++i)
{
//参数依次是:创建的线程id,线程参数,调用的函数,传入的函数参数
int ret = pthread_create(&tids[i], NULL, say_hello, NULL);
if (ret != 0)
{
cout << "pthread_create error: error_code=" << ret << endl;
}
}
//等各个线程退出后,进程才结束,否则进程强制结束了,线程可能还没反应过来;
pthread_exit(NULL);
}
Linux不包含pthread库,编译时使用:
$ g++ test.cpp -lpthread -o test.o
#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];// 用数组来保存i的值
int rc;
int i;
for( i=0; i < NUM_THREADS; i++ ){
cout << "main() : 创建线程, " << i << endl;
indexes[i] = i; //先保存i的值
// 传入的时候必须强制转换为void* 类型,即无类型指针
rc = pthread_create(&threads[i], NULL,
PrintHello, (void *)&(indexes[i]));
if (rc){
cout << "Error:无法创建线程," << rc << endl;
exit(-1);
}
}
pthread_exit(NULL);
}
可以像新建子线程传递参数,代码:
#include <iostream>
#include <cstdlib>
#include <pthread.h>
using namespace std;
#define NUM_THREADS 5
struct thread_data{
int thread_id;
char *message;
};
void *PrintHello(void *threadarg)
{
struct thread_data *my_data;
my_data = (struct thread_data *) threadarg;
cout << "Thread ID : " << my_data->thread_id ;
cout << " Message : " << my_data->message << endl;
pthread_exit(NULL);
}
int main ()
{
pthread_t threads[NUM_THREADS];
struct thread_data td[NUM_THREADS];
int rc;
int i;
for( i=0; i < NUM_THREADS; i++ ){
cout <<"main() : creating thread, " << i << endl;
td[i].thread_id = i;
td[i].message = (char*)"This is message";
rc = pthread_create(&threads[i], NULL,
PrintHello, (void *)&td[i]);
if (rc){
cout << "Error:unable to create thread," << rc << endl;
exit(-1);
}
}
pthread_exit(NULL);
}
连接与分离线程:
pthread_join(threadid, status);连接
pthread_detach(threadid);分离
join是三种同步线程的方式之一。另外两种分别是互斥锁(mutex)和条件变量(condition variable)。
调用pthread_join()将阻塞自己,一直到要等待加入的线程运行结束。
可以用pthread_join()获取线程的返回值。
一个线程对应一个pthread_join()调用,对同一个线程进行多次pthread_join()调用是逻辑错误。
join or detach
线程分两种:一种可以join,另一种不可以。该属性在创建线程的时候指定。
joinable线程可在创建后,用pthread_detach()显式地分离。但分离后不可以再合并。该操作不可逆。
为了确保移植性,在创建线程时,最好显式指定其join或detach属性。似乎不是所有POSIX实现都是用joinable作默认。
#POSIX:可移植操作系统接口(Portable Operating System Interface of UNIX)
pthread_detach()详解:
创建一个线程默认的状态是joinable, 如果一个线程结束运行但没有被join,则它的状态类似于进程中的Zombie Process,即还有一部分资源没有被回收(退出状态码),所以创建线程者应该调用pthread_join来等待线程运行结束,并可得到线程的退出代码,回收其资源(类似于wait,waitpid)
但是调用pthread_join(pthread_id)后,如果该线程没有运行结束,调用者会被阻塞,在有些情况下我们并不希望如此,比如在Web服务器中当主线程为每个新来的链接创建一个子线程进行处理的时候,主线程并不希望因为调用pthread_join而阻塞(因为还要继续处理之后到来的链接),这时可以在子线程中加入代码
pthread_detach(pthread_self())
或者父线程调用
pthread_detach(thread_id)(非阻塞,可立即返回)
这将该子线程的状态设置为detached,则该线程运行结束后会自动释放所有资源。
pthread_join():
调用pthread_join的线程会阻塞,直到指定的线程返回,调用了pthread_exit,或者被取消。
如果线程简单的返回,那么rval_ptr被设置成线程的返回值,参见范例1;如果调用了pthread_exit,则可将一个无类型指针返回,在pthread_join中对其进行访问,参见范例2;如果线程被取消,rval_ptr被设置成PTHREAD_CANCELED。
如果我们不关心线程的返回值,那么我们可以把rval_ptr设置为NULL。
#include <pthread.h>
#include <string.h>
#include <iostream>
using namespace std;
void *thr_fn1(void *arg)
{
cout<<"thread 1 returning.\n";
return((void *)1);
}
void *thr_fn2(void *arg)
{
printf("thread 2 exiting.\n");
return((void *)2);
}
int main()
{
pthread_t tid1,tid2;
void *tret;
pthread_create(&tid1,NULL,thr_fn1,NULL);
pthread_create(&tid2,NULL,thr_fn2,NULL);
pthread_join(tid1,&tret);
printf("thread 1 exit code %d\n",(int *)tret);
pthread_join(tid2,&tret);
printf("thread 2 exit code %d\n",(int *)tret);
exit(0);
}
pthread_join用于等待一个线程的结束,也就是主线程中要是加了这段代码,就会在加代码的位置卡主,直到这个线程执行完毕才往下走。
pthread_exit用于强制退出一个线程(非执行完毕退出),一般用于线程内部。
C++禁止将void指针随意赋值给其他指针。
#include <stdio.h>
#include <pthread.h>
void* thread1(void *s)
{
printf("This is a pthread1.\n");
printf("%s\n",(char *)s);
pthread_exit((void*)"the first return!"); //结束线程,返回一个值。
}
void* thread2(void *s)
{
printf("This is a pthread2.\n");
printf("%s\n",(char *)s);
pthread_exit((void*)"the second return!");
}
/**************main function ****************/
int main(void)
{
pthread_t id1,id2;
void *a1,*a2;
int i,ret1,ret2;
char s1[]="This is first thread!";
char s2[]="This is second thread!";
ret1=pthread_create(&id1,NULL,thread1,s1);
ret2=pthread_create(&id2,NULL,thread2,s2);
if(ret1!=0){
printf ("Create pthread1 error!\n");
return 1;
}
pthread_join(id1,&a1);
printf("%s\n",(char*)a1);
if(ret2!=0){
printf ("Create pthread2 error!\n");
return 1;
}
printf("This is the main process.\n");
pthread_join(id2,&a2);
printf("%s\n",*(int*)a2);
return 0;
}
pthread_attr_t 线程属性
pthread_attr_t attr;//创建
pthread_attr_init(&attr)//初始化
pthread_attr_setdatachstate(&attr, PTHREAD_CREATE_JOINABLE)//设置线程属性,PTHREAD_CREATE_DETACHED将导致使用attr创建的所有线程处于分离状态,而使用值PTHREAD_CREATE_JOINABLE将导致使用attr创建的所有线程处于可连接状态。