线程的创造和生命
目录
1. 创造新线程
1.1 线程ID
线程 | 进程 |
---|---|
pthread_t | pid_t |
pthread_self() | getpid() |
pthread_create() | fork() |
pthread_t:结构体(FreeBSD5.2、Mac OS10.3)/unsigned long int(linux)
/usr/include/bits/pthreadtypes.h
获取线程ID: pthread_self()
一个实例: 获取主线程ID
//获取主线程ID
#include "apue.h"
int main()
{
pid_t pid;
pthread_t tid;
pid = getpid();
tid = pthread_self();
printf("pid is %u, tid is %x\n", pid, tid);
return 0;
}
1.2 创建线程
pthread_create函数:
int pthread_create(pthread_t *restrict tidp,
const pthread_attr_t *restrict attr,
void *(*start_routine)(void *),
void *restrict arg)
第一个参数:新线程的id,如果成功则新线程的id回填充到tidp指向的内存
第二个参数:线程属性(调度策略,继承性,分离性…)
第三个参数:回调函数(新线程要执行的函数)
第四个参数:回调函数的参数
返回值:成功返回0,失败则返回错误码
编译时需要连接库libpthread
1.3 实例:创建线程,打印ID
/*getpid() 获取进程ID
*pthread_self() 获取县城ID
*
*int pthread_create(pthread_t *thread,
* const pthread_attr_t *attr,
* void *(*start_routine) (void *),
* void *arg);
*第一个参数,新线程id,创建成功系统回填
*第二个参数,新线程到属性,NULL为默认属性
*第三个参数,新线程到启动函数
*第四个参数,传递给新线程
*/
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
void print_id(char *s)
{
pid_t pid;
pthread_t tid;
pid = getpid();
tid = pthread_self();
printf("%s pid is %u, tid is 0x%x\n", s, pid, tid);
}
void *thread_fun(void *arg)
{
print_id(arg);
return (void *)0;
}
int main()
{
pthread_t ntid;
int err;
err = pthread_create(&ntid, NULL, thread_fun, "new thread");
if(err != 0 )
{
printf("create new thread failed\n");
return 0;
}
print_id("main thread :");
sleep(2);
return 0;
}
2. 线程的生命周期
2.1 初始线程/主线程
1、当c程序运行时,首先运行main函数。在线程代码中,这个特殊的执行流被称作初始线程或者主线程。你可以在初始线程中做任何普通线程可以做的事情。
2、主线程的特殊性在于,它在main函数返回的时候,会导致进程结束,进程内所有的线程也将会结束。这可不是一个好的现象,你可以在主线程中调用pthread_exit函数,这样进程就会等待所有线程结束时才终止。
3、主线程接受参数的方式是通过argc和argv,而普通的线程只有一个参数void*
4、在绝大多数情况下,主线程在默认堆栈上运行,这个堆栈可以增长到足够的长度。而普通线程的堆栈是受限制的,一旦溢出就会产生错误
2.2 线程的创建
1、主线程是随着进程的创建而创建
2、其他线程可以通过调用函数来创建,主要调用pthread_create
3、请注意,新线程可能在当前线程从函数pthread_create返回之前就已经运行了,甚至新 线程可能在当前线程从函数pthread_create返回之前就已经运行完毕了。
2.3 实例:创造一个线程,主线程打印奇数,新线程打印偶数,交替执行
//DESCRIPTION: 主线程打印奇数,新线程打印偶数,交替执行
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
int flag = 1;
void *thread_fun(void *arg)
{
int num = 1;
while(1)
{
if(flag==0)
{
//将flag置1,这样只有主线程打印一次后,新线程才能打印
flag=1;
printf("new thread print num: %d\n", num*2);
num++;
}
//睡眠,使主线程和新线程交替执行
sleep(1);
}
}
int main()
{
int err, num=0;
pthread_t tid;
err = pthread_create(&tid, NULL, thread_fun, NULL);
if(err != 0)
{
printf("create new thread failed \n");
return 0;
}
while(num<20)
{
//flag==1主线程打印
if(flag==1)
{
//将flag置0,这样只有新线程打印一次,主线程才能打印
flag = 0;
printf("main thread print num: %d\n", num*2+1);
num++;
}
//睡眠,使得主线程和新线程可以交替执行
sleep(1);
}
return 0;
}
2.4 线程的四个基本状态
状态 | 含义 |
---|---|
就绪 | 线程能够运行,但是在等待可用的处理器 |
运行 | 线程正在运行,在多核系统中,可能同时有多个线程在运行 |
阻塞 | 线程在等待处理器以外的其他条件 |
终止 | 线程从启动函数中返回,或者调用pthread_exit函数,或者被取消 |
回收:
线程的分离属性:
- 分离一个正在运行的线程并不影响它,仅仅是通知当前系统该线程结束时,其所属的资源可以回收。一个没有被分离的线程在终止时会保留它的虚拟内存,包括他们的堆栈和其他系统资源,有时这种线程被称为“僵尸线程”。创建线程时默认是非分离的
- 如果线程具有分离属性,线程终止时会被立刻回收,回收将释放掉所有在线程终止时未释放的系统资源和进程资源,包括保存线程返回值的内存空间、堆栈、保存寄存器的内存空间等。
- 终止被分离的线程会释放所有的系统资源,但是你必须释放由该线程占有的程序资源。由malloc或者mmap分配的内存可以在任何时候由任何线程释放,条件变量、互斥量、信号灯可以由任何线程销毁,只要他们被解锁了或者没有线程等待。但是只有互斥量的主人才能解锁它,所以在线程终止前,你需要解锁互斥量