一、线程的基本使用
正常情况下,一个进程只有一个一线程在运行。有了多线程之后,就可以使得程序在同一时间可以做多间不同的事情。
这样就可以利用起来很多被浪费掉的时间,例如执行阻塞任务,磁盘IO等等,这些等待的时间都可以被用来做其他的事情,这也就是多线程的用武之地。
1.1 线程标识
线程通过线程ID来标识,在linux环境中被定义为pthread_t
类型。在调试中,我们可以把它当作一个整形变量打印,但是实际上在严谨的场合不能这么干,它并不等于整形。
判断两个线程id是否相等要使用linux提供的函数接口:
#include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);
对线程而言,可以通过pthread_self()
函数来获得当前线程的ID,例如打印出一个函数中主线程的id:
#include <stdio.h>
#include <pthread.h>
int main() {
pthread_t tid;
tid = pthread_self();
printf("main thread: %08x\n", tid);
return 0;
}
编译运行:
main thread: 0x66744700
注意,多线程程序编译时要加-lpthread选项
1.2 创建一个函数
创建一个线程的函数原型:
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
各个参数的含义:
thread
:用来接收新创建的线程ID,该参数不能省略,不能写成NULLattr
:设置线程的属性,一般为NULLstart_routine
:线程启动的函数,是一个函数指针,线程启动后将调用这个函数arg
:自定义参数,会作为线程函数的启动参数传入
以下是一个简单的创建线程的示例,创建一个线程并打印出它当前的进程id和线程id:
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
typedef unsigned int uint;
void f(const char *s) {
pid_t pid = getpid();
pthread_t tid = pthread_self();
// 打印出当前的进程id和线程id
printf("%s pid is %d, tid is 0x%x\n", s, pid, (uint)tid);
}
void *thread_start(void *arg) {
f((char*)arg);
return 0;
}
int main(){
int err;
pthread_t tid;
// 创建线程
err = pthread_create(&tid, 0, thread_start, (void*)"new thread:");
if (err != 0) {
fprintf(stderr, "pthread_create error: %s", strerror(err));
return 0;
}
printf("main thread: pid is %d, child thread is 0x%x.\n", (int)getpid(), (uint)tid);
// 等待线程执行完毕
sleep(1);
return 0;
}
运行结果:
main thread: pid is 18267, child thread is 0xe33ba700.
new thread: pid is 18267, tid is 0xe33ba700
根据两个线程打印出来的pid可以看到:多线程实际上还是处于同一个进程的。
二、线程终止
2.1 pthread_exit()
pthread_exit()
的作用就是退出当前线程,并设置退出的返回值。定义为:
#include <pthread.h>
void pthread_exit(void *retval);
retval
参数表示线程的退出状态。
线程也可以使用return来返回,但是在线程定义了清理函数的时候,如果使用return返回,清理函数将不会被执行。
2.2 pthread_join()
上面代码的主函数中,最后面有一个sleep(1)
来等待子线程退出,这么做实际上是不合理的,因为线程和进程一样,执行的时机是不确定的,1S之后线程可能还没有执行完。
如果要在主线程等待子线程执行完毕的话,可以通过pthread_join()
函数来完成,其定义为:
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
参数含义:
thread
:线程idretval
:线程的返回码
调用这个函数后,线程会一直阻塞,直到指定线程退出。如果调用时线程已经处于结束状态,那么函数会立即返回。
如果指定的线程被取消了,retval
的值将会是PTHREAD_CANCELED
。不关心线程返回值的话,可以直接把retval
设为NULL。
注意,如果线程被分离了(执行了pthread_detach()
),执行pthread_join()
无效。
以下是一个调用pthread_exit()
和pthread_join()
的示例:
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
typedef unsigned int uint;
void *func1(void *arg) {
pthread_t tid;
tid = pthread_self();
printf("this is func1(), tid = 0x%08x\n", tid);
return (void *)111;
}
void *func2(void *arg) {
pthread_t tid;
tid = pthread_self();
printf("this is func2(), tid = 0x%08x\n", tid);
pthread_exit((void *)222);
}
// 创建一个线程,并打印出对应的信息
int pthread_create_ex(pthread_t *tid, void *(pf)(void*)) {
int err;
err = pthread_create(tid, 0, pf, NULL);
if (err != 0) {
fprintf(stderr, "pthread_create error: %s", strerror(err));
} else {
printf("main thread: child thread is 0x%x\n", tid);
}
return err;
}
// 回收一个线程,并打印相关信息
int pthread_join_ex(pthread_t tid) {
int err, retval;
err = pthread_join(tid, (void *)&retval);
if (err != 0) {
fprintf(stderr, "pthread_join error: %s", strerror(err));
} else {
printf("thread 0x%x has exit, code %d\n", tid, retval);
}
return err;
}
int main(){
int err;
pthread_t tid1, tid2;
err = pthread_create_ex(&tid1, func1);
if (err != 0)
return -1;
err = pthread_create_ex(&tid2, func2);
if (err != 0)
return -1;
err = pthread_join_ex(tid1);
if (err != 0)
return -1;
err = pthread_join_ex(tid2);
if (err != 0)
return -1;
return 0;
}
这里的代码用两个函数作为不同的线程开始,分别通过return
和pthread_exit
来返回,运行结果:
可以看到,pthread_join()
都能拿到返回值,那这两者是否有区别呢?下面将会继续探究。
2.2 pthread_cancel()
pthread_cancel()
函数可以取消同一进程中的其他线程:
#include <pthread.h>
int pthread_cancel(pthread_t thread);
参数就是希望取消的线程id,这会使得被取消的线程返回PTHREAD_CANCELED
。
#include <stdio.h>
#include <pthread.h>
void *f1(void *arg) {
int i = 0;
while (i++ < 5) {
printf("second %d\n", i);
sleep(1);
}
pthread_exit((void *)99);
}
void *f2(void *arg) {
pthread_t tid;
tid = *(pthread_t *)arg;
sleep(1);
printf("pthread_cancel --> 0x%08x\n", tid);
pthread_cancel(tid);
pthread_exit((void *)0);
}
int main() {
pthread_t tid1, tid2;
pthread_t ret1, ret2;
pthread_create(&tid1, NULL, f1, NULL);
pthread_create(&tid2, NULL, f2, (void *)&tid1);
pthread_join(tid1, (void *)&ret1);
pthread_join(tid2, (void *)&ret2);
printf("tid1: %08x, exit status: %d\n", tid1, ret1);
printf("tid2: %08x, exit status: %d\n", tid2, ret2);
printf("PTHREAD_CANCELED = %d\n", PTHREAD_CANCELED);
return 0;
}
以上是一个简单的调用示例,产生两个线程,一个线程每隔一秒打印一次,另一个线程则在一秒后关闭该线程。
主线程中等待退出并打印返回码,结果如下所示:
被取消掉的线程91ef7700
并没有返回其函数内定义的99,而是-1
,也就是PTHREAD_CANCELED
的值。
此处评论已关闭