线程概念
典型的UNIX进程可以看作只有一个控制线程:一个进程在某一个时刻只做一件事情。有了多个线程后,在程序中可以时线程处理各自独立的任务,这样做的好处有许多。
1. 简化处理异步事件的代码;
2. 线程比进程更方便做数据共享;
3. 可以提高程序的吞吐量;
4. 方便改善程序响应时间
每个线程都包含有表示执行环境所必需的信息,其中包括进程中标识线程的线程ID、一组寄存器值、栈、调度优先级和策略、信号屏蔽字、errno变量以及线程私有数据。一个进程的所有信息对该进程的所有线程都是共享的,包括可执行程序的代码、程序的全局内存和堆内存以及文件描述符。
线程标识
线程ID用phtread_t数据类型来表示(在linux中是用无符号长整型),在不同的操作系统中,pthread_t具体的数据类型不同,所有,在可移植的软件中,比较线程的值,使用的是pthread_equal函数。程序要获取自己的线程ID时,使用的是pthread_self函数。
#include <pthread.h>
int phtread_equal(pthread_t tid1, pthread_t tid2); #若相等,返回非0,相等则返回0
pthread_t pthread_self(void); #返回调用线程的线程ID
线程创建
使用pthread_create函数创建新线程
#include <pthread.h>
int pthread_create(pthread_t *restrict tidp, const pthread_t *restrict attr,
void *(*start_rtn) (void *), void *restrict arg);
- 若成功,返回0,否则,返回错误编号
- 成功返回时,线程ID会被设置为tidp指向的内存单元
- attr用于设置线程属性,不需要时,可设置为NULL
- 新创建的线程从start_rtn函数的起始地址开始运行,函数的参数为arg,如果要传给线程的参数不止一个,可以用一个结构体包括所有参数,然后将结构体的地址传给线程
- 返回的错误码是线程私有数据
代码示例:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
pthread_t ntid;
void printids(const char *s)
{
pid_t pid;
pthread_t tid;
pid = getpid();
tid = pthread_self();
printf("%s pid %lu tid %lu (0x%lx)\n", s, (unsigned long)pid, (unsigned long)tid, (unsigned long)tid);
}
void * thr_fn(void *arg)
{
printids("new thread: ");
return ((void *)0);
}
int main(void)
{
int err;
err = pthread_create(&ntid, NULL, thr_fn, NULL);
if (err) {
printf("can't create thread\n");
}
printids("main thread: ");
sleep(1);
return 0;
}
- 主线程需要休眠,否则主线程return后,整个程序退出,新的线程跟着退出,无法按照我们设计的打印
- 虽然ntid是全局变量,但线程取线程ID的时候,使用pthread_self,而不是直接使用ntid,因为如果thr_fn在pthread_create返回之前就运行了,那见到的是未被初始化的ntid,这不是正确的线程ID
输出:
main thread: pid 786 tid 140693894424320 (0x7ff5d9996700)
new thread: pid 786 tid 140693886129920 (0x7ff5d91ad700)
- pid相同,但线程id不同。