线程和进程
进程是系统分配资源的基本单位,线程是CPU调度的基本单位;启动一个程序,可能开启一个或多个进程,一个进程可以拥有多个线程,每个进程都有一个主线程。
线程的相关函数
创建一个线程:
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
函数功能:在进程里创建一个新的线程
函数参数:
thread----->一个线程id的地址
attr----->线程的属性
start_routine----->线程执行函数
arg----->传递给程序函数的唯一的一个参数
返回值:成功返回0,失败返回错误码。
主动挂起一个线程:
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
函数功能:使调用此函数的线程被挂起,等待线程id为thread的线程终止后,回收这个线程的资源;被挂起的线程的属性必须是连接属性(即将pthread_create()函数的第二个参数设置为NULL,就默认这个线程是连接状态)。
参数:
thread----->被等待的线程id
retval----->不为NULL时,retval指向为被等待线程终止时的返回值
返回值:成功返回0,失败返回错误码
终止一个线程:
可以使用return语句和exit()函数:
在主线程里使用来二者其一的话,会使得整个进程终止,其他还在运行的线程也会被终止;
除主线程外的线程使用二者其一,会使线程主动终止,不会影响主线程。
#include <pthread.h>
void pthread_exit(void *retval);
函数功能:主动结束调用此函数的线程
参数:
retval----->指向线程返回值的指针
返回值:没有返回值
#include <pthread.h>
int pthread_cancel(pthread_t thread);
函数功能:向一个线程发送终止信号
参数:
thread----->接受终止信号的线程id
返回值:成功返回0;失败返回非0值。
(向线程发送终止信号后,并不一定会使的线程终止,可以用int pthread_setcancelstate(int state, int *oldstate)函数来设置线程对终止信号的响应,第一个参数state为PTHREAD_CANCEL_ENABLE(缺省)时执行终止信号,为PTHREAD_CANCEL_DISABLE时忽略终止信号继续执行,第二个参数不为NULL时,就用oldstate保留线程的状态信息。)
设置线程的属性:
pthread_attr_t attr 线程属性
线程为连接属性的话,一般由其他线程调用pthread_join来释放线程的资源;线程为分离属性的话,线程结束后,由系统释放线程资源。
①初始化线程属性变量:
int pthread_attr_init(pthread_attr_t *attr);
②设置线程属性变量为分离态或非分离态:
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
参数detachstate为PTHREAD_CREATE_DETACHED(分离态)或PTHREAD_CREATE_JOINABLE(非分离态)
③利用pthread_create来创建线程:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
④销毁线程属性变量:
int pthread_attr_destroy(pthread_attr_t *attr); //销毁线性属性变量
打印线程id:
#include <pthread.h>
pthread_t pthread_self(void);
函数功能:打印线程id
参数:没有参数
返回值:成功返回线程id;不会执行失败。
练习:
1、创建一个线程,在该线程里打印命令行参数,在主线程里利用pthread_join来实现线程间同步,并释放被等待线程的资源:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
struct main_arg
{
int num_arg;
char **pp_arg;
};
void *thread_func(void *arg)
{
printf("thread start...\n");
struct main_arg *p = (struct main_arg *)arg;
for(int i = 0; i < p->num_arg; ++i)
printf("%s\n", *(p->pp_arg + i) ); //打印命令行参数
printf("thread end...\n");
pthread_exit(NULL);
}
int main(int argc, char const *argv[])
{
struct main_arg arg;
arg.num_arg = argc ;
arg.pp_arg = (char**)&argv[0];
pthread_t tid;
int ret = pthread_create(&tid, NULL, thread_func, (void*)&arg);
if(ret != 0)
{
perror("pthread_create");
return -1;
}
pthread_join(tid, NULL); //主线程挂起,等待线程结束,回收线程的资源
return 0;
}
2、创建一个线程,将这个线程的属性设置为分离属性,在该线程里打印命令行参数:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
struct main_arg
{
int num_arg;
char **pp_arg;
};
void *thread_func(void *arg)
{
printf("thread start...\n");
struct main_arg *p = (struct main_arg *)arg;
for(int i = 0; i < p->num_arg; ++i)
printf("%s\n", *(p->pp_arg + i) ); //打印命令行参数
printf("thread end...\n");
pthread_exit(NULL);
}
int main(int argc, char const *argv[])
{
struct main_arg arg;
arg.num_arg = argc ;
arg.pp_arg = (char**)&argv[0];
pthread_attr_t attr; //线程属性
pthread_attr_init(&attr); //初始化线程属性变量
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); //将线程属性设置为分离态
pthread_t tid;
int ret = pthread_create(&tid, &attr, thread_func, (void*)&arg); //创建分离态的线程
if(ret != 0)
{
perror("pthread_create");
return -1;
}
sleep(1); //主线程挂起1秒,等待新创建的线程结束
pthread_attr_destroy(&attr); //销毁线程属性变量
return 0;
}
结果分析:
练习2里新创建的线程结束后虽然可以由系统自动释放其资源,但是因为将新创建的线程被设置为了分离态,所以,新线程和主线程之间的执行先后顺序不确定。而使用pthread_join就可以即完成线程之间的同步,又可以保证结束的线程的资源被及时回收。