创建线程后进程的地址空间没有变化,该进程退化为主线程。
创建出的子线程和主线程共用一块地址空间,但他们有各自独立的PCB,而子线程的PCB是从主线程拷贝来的。
子线程在不设置分离属性的前提下,必须由主线程来释放其PCB。
主线程程和子线程除了
不共享用户空间的
栈,其他的都共享。例如,假如有五个线程,则用户空间的栈被平均分成五份,各自使用。所以,我们也可以通过全局变量或者堆上的数据进行线程间的通信。
创建线程
#include
int pthread_create(pthread_t thread, const pthread_attr_t* attr, void*(*start_routine)(void*), void* arg))
参数:
thread: pthread_t 实际上是个无符号长整型,
thread作为传出参数,若线程创建成功后,该线程的ID会被写入该参数中
attr: 线程的属性,一般传NULL。(之后我们会用它来设置线程分离属性)
strt_routine: 线程处理函数,该函数就是子线程要做的事情。
arg:线程处理函数的参数,为线程处理函数传参。
返回值:
成功返回0,失败返回错误号。
注意:该错误号不是我们平时所说的 errno,线程中不能使用 perror() 函数。但我们可以使用下面这个函数:
char* strerror(int errnum)
该函数的返回值是 char* 类型的,所以可以通过它来打印与创建线程的错误号对应的错误信息。
退出线程
#include
void pthread_exit(void* retval );
参数:
retval : 用来保存一个错误信息,该参数必须指向全局变量或者堆内存。不能指向一个栈地址,因为该子线程退出时,该子线程对应的栈空间就被回收了。
阻塞等待线程退出,获取线程的退出状态,并且回收其PCB
int pthread_join(pthread_t thread, void** retval);
参数:
thread : 要回收的子线程的线程ID
retval :传出参数,获取线程退出时携带的状态信息,和
pthread_exit() 函数的参数指向同一块内存地址。
返回值:
成功返回0,失败返回错误号。
线程ID
pthread_t pthread_self( void );
该函数总是执行成功,返回调用该函数的线程的线程ID
杀死(取消)线程
int
pthread_cancel(pthread_t
thread);
参数:
thread : 要杀死(取消)的子线程的线程ID
返回值:
成功返回 0,失败返回一个非零的错误号码。
注意事项:
在要杀死的子线程对应的处理函数的内部,必须做过一次系统调用,否则将不能将该子线程杀死。
如果不清楚什么是系统调用,可使用
pthread_testcancel() 函数来给子线程设置一个取消点,该函数就是个系统调用。
线程分离
int
pthread_detach(pthread_t
thread);
参数:
thread : 要回收的子线程的线程ID
返回值:
成功返回0,失败返回错误号。
注意:在子线程中调用该函数后就不需要在主线程中使用
pthread_join() 函数了,
设置线程分离后,子线程会自己回收自己的PCB,不用依赖父线程回收。不过有个极端的情况是,也许在子线程中还没来得及调用这个函数设置线程分离,线程就结束了,导致线程的PCB无法被回收。解决的办法是,直接利用
pthread_create() 函数的第二个参数,在创建线程的时候就对其设置线程分离的属性,下面就是该方法的介绍。
在创建线程时设置分离属性
1、 先根据参数创建一个 pthread_attr_t 类型的变量,如: pthread_attr_t attr ;
2、线程属性操作函数
对线程属性变量的初始化:
int pthread_attr_init(pthread_attr_t* attr);
设置线程分离属性:
int pthread_attr_setdetachstate(pthread_attr_t* attr, int detachstate);
参数:
attr: 把属性设置给该参数
detachstate: 线程属性,有两个用来设置分离状态的宏
PTHREAD_CREATE_DETACHED (分离)
PTHREAD_CREATE_JOINABLE(非分离)
3、释放线程资源函数
int pthread_attr_destroy(pthread_attr_t* attr);
以上函数的综合举例(代码)
1、不设置分离属性的创建线程的代码
该代码让主线程打印出其线程ID,然后循环五次打印信息,然后调用 pthread_join() ,在该处阻塞等待子线程结束。子线程也是打印出其线程ID,但虚幻五次打印信息时,每次间隔一秒钟,这样,可以保证子线程绝对比主线程结束的要慢。
当子线程结束时,主线程从 pthread_join() 处解除阻塞,最后打印出子线程退出的状态结束。
#include
#include
#include
#include
#include
int global_exit_state = 777; //该全局变量用作子线程的退出状态
void* func(void* arg) //线程处理函数
{
printf("child thread number: %lu
", pthread_self()); //打印子线程的线程ID
int i = 0;
for(; i < 5; i++)
{
sleep(1);
printf("child i = %d
", i);
}
pthread_exit(&global_exit_state); //退出子线程
}
int main()
{
//线程id变量
pthread_t pthid;
//创建子线程
int ret = pthread_create(&pthid,NULL, func, NULL);
if(ret != 0)
{
printf("error number: %d
", ret);
// 根据错误码,打印错误信息
printf("%s
", strerror(ret));
}
printf("parent thread id: %lu
", pthread_self()); //主线程的线程ID
int i = 0;
for(; i < 5; ++i)
{
printf("parent i = %d
", i);
}
void* ptr = NULL;
pthread_join(pthid, &ptr);
printf("exit status:%d
", *(int*)(ptr));
pthread_exit(NULL);
}
运行结果为:
2、设置分离属性的创建线程的代码
设置线程分离后,子线程的PCB就不用主线程来释放了,它能够自己释放,所以不需要 pthread_join() 函数。
#include
#include
#include
#include
#include
int global_exit_state = 777;
void* func(void* arg) //线程处理函数
{
printf("child thread number: %lu
", pthread_self());
int i = 0;
for(; i < 5; i++)
{
sleep(1);
printf("child i = %d
", i);
}
pthread_exit(&global_exit_state);
}
int main()
{
//创建一个子线程,线程id变量
pthread_t pthid;
//初始化线程的属性
pthread_attr_t attr;
pthread_attr_init(&attr);
//设置线程分离的属性
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
//创建线程的时候设置线程分离
int ret = pthread_create(&pthid,&attr, func, NULL);
if(ret != 0)
{
printf("error number: %d
", ret);
// 根据错误码,打印错误信息
printf("%s
", strerror(ret));
}
printf("parent thread id: %lu
", pthread_self());
int i = 0;
for(; i < 5; ++i)
{
printf("parent i = %d
", i);
}
//释放资源
pthread_attr_destroy(&attr);
printf("parent exit
");
pthread_exit(NULL);
}
运行结果: