基本函数
线程创建 pthread_create
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
//返回值:成功返回0,失败返回错误编号
pthread_t *thread:线程ID,由函数pthread_self()获取,类似获取进程pid使用getpid()函数;
const pthread_attr_t *attr:用于定制各种不同的线程属性,暂可以把它设置为NULL,以创建默认属性的线程;
void *(*start_routine) (void *):线程中执行函数。新创建的线程从start_rtn函数的地址开始运行,该函数只有一个无类型指针参数arg
void *arg:执行函数中中参数。如果需要向start_rtn函数传递的参数不止一个,那么需要把这些参数放到一个结构体中,然后把这个结构的地址作为arg参数传入
线程退出 pthread_exit
单个线程可以通过以下三种方式退出,在不终止整个进程的情况下停止它的控制流:
1)线程只是从启动例程中返回,返回值是线程的退出码。
2)线程可以被同一进程中的其他线程取消。
3)线程调用pthread_exit:
#include <pthread.h>
int pthread_exit(void *rval_ptr);
rval_ptr:是一个无类型指针,与传给启动例程的单个参数类似。进程中的其他线程可以通过调用pthread_join函数访问到这个指针。
线程等待 pthread_join
#include <pthread.h>
int pthread_join(pthread_t thread, void **rval_ptr);
// 返回:若成功返回0,否则返回错误编号
调用这个函数的线程将一直阻塞,直到指定的线程调用pthread_exit、从启动例程中返回或者被取消。
如果例程只是从它的启动例程返回,rval_ptr将包含返回码。
如果线程被取消,由rval_ptr指定的内存单元就置为PTHREAD_CANCELED。
①可以通过调用pthread_join自动把线程置于分离状态,这样资源就可以恢复。如果线程已经处于分离状态,pthread_join调用就会失败,返回EINVAL。
②如果对线程的返回值不感兴趣,可以把rval_ptr置为NULL。在这种情况下,调用pthread_join函数将等待指定的线程终止,但并不获得线程的终止状态。
简单使用
我们先编写一个简单的程序感受一下多线程
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void *thread_func(void *arg)
{
int i = 0;
while (i < 10) {
printf("New thread arg: %d, %d\n", *(int *)arg, i);
i++;
}
}
int main()
{
pthread_t thread_id;
int arg = 123;
int ret;
ret = pthread_create(&thread_id, NULL, thread_func, (void *)&arg);
if (ret != 0) {
printf("Failed to create new thread");
exit(EXIT_FAILURE);
}
printf("Main thread: New thread created with ID %ld\n", thread_id);
pthread_join(thread_id, NULL);
}
输出:
Main thread: New thread created with ID 1
New thread arg: 123, 0
New thread arg: 123, 1
New thread arg: 123, 2
New thread arg: 123, 3
New thread arg: 123, 4
New thread arg: 123, 5
New thread arg: 123, 6
New thread arg: 123, 7
New thread arg: 123, 8
New thread arg: 123, 9
首先我们在main中创建了一个thread_func线程,并传入了arg参数,main函数使用pthread_join方法等待thread_func线程的循环结束后再退出。
注意:如果不在main中写pthread_join,则main函数不会等待thread_func执行结束而结束,而是会继续往下运行直到结束后直接退出,不管thread_func是否运行结束。
注意到:如果我们多次调用pthread_create会发生什么呢?
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void *thread_func(void *arg)
{
int i = 0;
while (i < 10) {
printf("New thread arg: %d, %d\n", *(int *)arg, i);
i++;
}
}
int main()
{
pthread_t thread_id;
int arg = 123;
int ret;
ret = pthread_create(&thread_id, NULL, thread_func, (void *)&arg);
if (ret != 0) {
printf("Failed to create new thread");
exit(EXIT_FAILURE);
}
ret = pthread_create(&thread_id, NULL, thread_func, (void *)&arg);
if (ret != 0) {
printf("Failed to create new thread");
exit(EXIT_FAILURE);
}
printf("Main thread: New thread created with ID %ld\n", thread_id);
pthread_join(thread_id, NULL);
}
在这里我们多次调用pthread_create,输出如下
Main thread: New thread created with ID 2
New thread arg: 123, 0
New thread arg: 123, 0
New thread arg: 123, 1
New thread arg: 123, 2
New thread arg: 123, 3
New thread arg: 123, 1
New thread arg: 123, 2
New thread arg: 123, 4
New thread arg: 123, 3
New thread arg: 123, 5
New thread arg: 123, 4
New thread arg: 123, 5
New thread arg: 123, 6
New thread arg: 123, 7
New thread arg: 123, 6
New thread arg: 123, 7
New thread arg: 123, 8
New thread arg: 123, 9
New thread arg: 123, 8
New thread arg: 123, 9
发现其实创建了两个线程,而且多次运行这个程序后会发现每次的输出都不一样。
那如果我们只需要存在一个线程呢?
最简单的方法就是设置一个标志变量,记录线程是否启动
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define TRUE 1
#define FALSE 0
#define SUCCESS 0
#define FAIL -1
static int isStart = FALSE;
pthread_t thread_id;
void *thread_func(void *arg)
{
int i = 0;
while (i < 10) {
printf("New thread arg: %d, %d\n", *(int *)arg, i);
usleep(100000);
i++;
}
}
int Init(void *arg){
if (isStart){
printf("Failed to create new thread : thread is started\n");
return FAIL;
}else {
int ret;
isStart = TRUE;
ret = pthread_create(&thread_id, NULL, thread_func, (void *)arg);
if (ret != SUCCESS) {
printf("Failed to create new thread");
return FAIL;
}
return SUCCESS;
}
}
int main()
{
int arg = 123;
int ret;
ret = Init(&arg);
if (ret == FAIL){
printf("init fail!\n");
}
ret = Init(&arg);
if (ret == FAIL){
printf("init fail!\n");
}
printf("Main thread: New thread created with ID %ld\n", thread_id);
pthread_join(thread_id, NULL);
}
输出:
Failed to create new thread : thread is started
init fail!
Main thread: New thread created with ID 1
New thread arg: 123, 0
New thread arg: 123, 1
New thread arg: 123, 2
New thread arg: 123, 3
New thread arg: 123, 4
New thread arg: 123, 5
New thread arg: 123, 6
New thread arg: 123, 7
New thread arg: 123, 8
New thread arg: 123, 9
注意!!!
此方法只能在单个进程的场景下使用,如果存在多个进程都会调用这里的接口,则仍然会在每个进程中存在这样的一个线程,可能导致出现问题。
笔者目前想到的规避方法是:使用内核线程,在内核中通过全局变量的方法控制只存在一个内核线程