创建线程
#include <pthread.h>
int pthread_create(pthread_t * thread,
const pthread_attr_t * attr,
void *(*start_routine)(void*), void * arg);
返回值:成功返回0,失败返回错误号。
thread:成功返回后,新创建的线程的id被填写到 thread
参数所指向的内存单元。线程id的类型是pthread_t
,它只在当前进程中保证是唯一的,在不同的系统中pthread_t
这个类型有不同的实现,它可能是一个整数值,也可能是一个结构体,也可能是一个地址,所以不能简单地当成整数用printf
打印,调用pthread_self()
可以获取当前线程地id。
attr:表示线程属性,这里暂不讨论。
start_routine和arg:新线程所指向的代码由函数指针start_routine
决定。start_routine
函数接收一个参数arg
,该参数的类型为void *
,这个指针按什么类型解释由调用者自己定义,start_routine
的返回值类型也是void *
,这个指针的函数的含义同样由调用者自己定义。
#include <pthread.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
static void thread_func(const char *arg)
{
printf("arg=%s\n", arg);
pthread_t tid = pthread_self();
printf("new thread pid=%u\n", (uint32_t)tid);
}
int main(void)
{
pthread_t id;
int ret = pthread_create(&id, NULL, thread_func, "i am iron man");
if (0 != ret)
{
printf("pthread_carete error=%s", strerror(ret));
}
printf("new thread pid=%u\n", (uint32_t)id);
while (1)
{
usleep(1000);
}
return 0;
}
输出结果为:
new thread pid=1
arg=i am iron man
new thread pid=1
可知,在window上,thread_t类型是一个整型值。
终止线程
如果需要终止某个线程而不终止整个进程,可以有三种方法:
- 从线程函数
return
。这种方法对主线程不适用,从main
函数return
相当于调用exit
。 - 一个线程可以调用
pthread_cancel
终止同一进程中地另一个线程。 - 线程可以调用
pthread_exit
终止自己。
在介绍终止之前,我们先介绍一下pthread_join
函数。
#include <pthread.h>
int pthread_join(pthread_t thread, void **value_ptr);//返回值:成功返回0,失败返回错误号。
调用该函数地线程将挂起等待,直到id为thread
的线程终止。thread
线程以不同的方法终止,通过pthread_join
得到的终止状态时不同的,总结如下:
- 如果
thread
线程通过return
返回,value_ptr
所指向的单元里存放的是thread
线程函数的返回值。 - 如果
thread
线程被别的线程调用thread_cancel
异常终止掉,value_ptr
所指向的单元里存放的是常数PTHREAD_CANCELED
。 - 如果
thread
线程是自己调用pthread_exit
终止的,value_ptr
所指向的单元里存放的是传给pthread_exit
的参数。
如果对thread
线程的终止状态不感兴趣,可以传NULL
给value_ptr
参数。
一般情况下,线程终止后,其终止状态一直保留到其他线程调用pthread_join
获取它的状态为止。但是线程也可以被设置为detach
状态,这样的线程一旦终止就立刻回收它占用的所有资源,而不保留终止状态。不能对一个已经处于detach
状态的线程调用pthread_join
,这样的调用将返回EINVAL
。对一个尚未detach
的线程调用pthread_join
或pthread_detach
都可以把该线程设置为detach状态,也就是说,不能对同一线程调用两次pthread_join
,或者如果已经对一个线程调用了pthread_detach
就不能再调用pthread_join
了。
#include <pthread.h>
int pthread_detach(pthread_t tid);//返回值:成功返回0,失败返回错误号。
#include <pthread.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
static void *thread1_func(const char *arg)
{
printf("arg=%s\n", arg);
pthread_t tid = pthread_self();
printf("new thread pid=%u\n", (uint32_t)tid);
return (void *)0xFF;
}
static void *thread2_func(const char *arg)
{
printf("arg=%s\n", arg);
pthread_t tid = pthread_self();
printf("new thread pid=%u\n", (uint32_t)tid);
pthread_exit((void *)0xFE);
}
static void *thread3_func(const char *arg)
{
printf("arg=%s\n", arg);
pthread_t tid = pthread_self();
printf("new thread pid=%u\n", (uint32_t)tid);
while (1)
{
printf("%s running\n", arg);
usleep(5000 * 100);
}
}
int main(void)
{
pthread_t id1;
int ret1 = pthread_create(&id1, NULL, thread1_func, "i am thread1");
if (0 != ret1)
{
printf("pthread_carete error=%s", strerror(ret1));
}
void *thread1_ret;
pthread_join(id1, &thread1_ret);
printf("thread1_ret=%d\n", (int)thread1_ret);
pthread_t id2;
int ret2 = pthread_create(&id2, NULL, thread2_func, "i am thread2");
if (0 != ret2)
{
printf("pthread_carete error=%s", strerror(ret2));
}
void *thread2_ret;
pthread_join(id2, &thread2_ret);
printf("thread2_ret=%d\n", (int)thread2_ret);
pthread_t id3;
int ret3 = pthread_create(&id3, NULL, thread3_func, "i am thread3");
if (0 != ret3)
{
printf("pthread_carete error=%s", strerror(ret3));
}
void *thread3_ret;
sleep(1);
pthread_cancel(id3);
pthread_join(id3, &thread3_ret);
printf("thread3_ret=%d\n", (int)thread3_ret);
while (1)
{
usleep(1000);
}
return 0;
}
输出为:
arg=i am thread1
new thread pid=1
thread1_ret=255
arg=i am thread2
new thread pid=3
thread2_ret=254
arg=i am thread3
new thread pid=4
i am thread3 running
i am thread3 running
i am thread3 running
i am thread3 running
i am thread3 running
i am thread3 running
i am thread3 running
i am thread3 running
i am thread3 running
i am thread3 running
i am thread3 running
i am thread3 running
i am thread3 running
i am thread3 running
i am thread3 running
i am thread3 running
i am thread3 running
i am thread3 running
i am thread3 running
i am thread3 running
我们可以看到,线程1和线程2的终止与我们的预期一样,但是线程3使用pthread_cancel()
似乎没有真正终止线程。那这里就需要引用另外两个概念了。
线程终止状态
@state:
PTHREAD_CANCEL_ENABLE:
线程对cancel信号立即有反应,将设置为CANCEL状态 (默认)
PTHREAD_CANCEL_DISABLE:
如果线程state为不可取消,线程不理会信号,继续执行,而使用cancel函数的线程会一直阻塞到可取消状态
@oldstate:
NULL:state写入有效,即当前只想设置属性,而不关心原来的属性
不为NULL:state不写入,保持原有的设定,且获取原来的属性
return:
成功返回0,错误返回errno
EINVAL:无效的state
int pthread_setcancelstate(int state,int *oldstate)
线程终止类型
@type:
PTHREAD_CANCEL_DEFERRED:
运行到下一个取消点就退出 (默认)
PTHREAD_CANCEL_ASYNCHRONOUS:
直接退出
@oldtyoe:
NULL:type写入有效,即当前只想设置属性,而不关心原来的属性
不为NULL:type不写入,保持原有的设定,且获取原来的属性
return:
成功返回0,错误返回errno
EINVAL:无效的type
int pthread_setcanceltype(int type,int *oldtype)
所以,如下想要使用pthread_cancel
终止线程,那么我们必须在线程函数中调用
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
当我们把线程函数改成:
static void *thread3_func(const char *arg)
{
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
printf("arg=%s\n", arg);
pthread_t tid = pthread_self();
printf("new thread pid=%u\n", (uint32_t)tid);
while (1)
{
printf("%s running\n", arg);
usleep(5000 * 100);
}
}
那么就可以正常终止了,输出结果如下:
arg=i am thread1
new thread pid=1
thread1_ret=255
arg=i am thread2
new thread pid=3
thread2_ret=254
arg=i am thread3
new thread pid=4
i am thread3 running
i am thread3 running
thread3_ret=-559038737