类Unix系统中,早期没有线程的概念,80年代才引入,借助进程机制实现出了线程的概念,因此在这类系统中,进程和线程关系密切:
ps -Lf pid
#include
pthread_t pthread_self(void);
获取线程号
无
调用函数的线程的线程id
#include
#include
#include
int main(int arg,char *args[])
{
printf("当前线程的线程id是%ld\n", pthread_self());
getchar();
return 0;
}
#include
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
创建一个线程
thread: 线程标识符地址
attr:线程属性结构体地址,通常设置为 NULL
start_routine:线程函数的入口地址
art:传给线程函数的参数
成功:0
失败:非0
#include
#include
#include
#include
int main(int arg,char *args[])
{
void * func(void *arg)
{
while (1)
{
printf("在子线程%ld中\n", pthread_self());
sleep (1);
}
return NULL;
}
pthread_t thread1;
int res = pthread_create(&thread1, NULL, func, NULL);
printf("res=%d\n",res);
printf("当前主线程的线程id是%ld\n", pthread_self());
printf("创建的子线程id是%ld\n", thread1);
getchar();
return 0;
#include
#include
#include
#include
int main(int arg,char *args[])
{
void * func(void *arg)
{
while (1)
{
printf("在子线程%ld中\n", pthread_self());
printf("传来的参数是%s\n", (char *)arg);
sleep (1);
}
return NULL;
}
pthread_t thread1;
int res = pthread_create(&thread1, NULL, func, "test123");
printf("res=%d\n",res);
printf("当前主线程的线程id是%ld\n", pthread_self());
printf("创建的子线程id是%ld\n", thread1);
getchar();
return 0;
}
#include
#include
#include
#include
void *func1(void *args)
{
int *p = (int *)args;
while(1)
{
printf("fun1中子线程%ld中参数为%d\n",pthread_self(), *p);
*p = *p+1;
sleep(1);
}
return NULL;
}
int main(int arg,char *args[])
{
pthread_t lwp1;
pthread_t lwp2;
int param1 = 1;
pthread_create(&lwp1, NULL,func1,¶m1);
pthread_create(&lwp2, NULL,func1,¶m1);
pthread_join(lwp1,NULL);
pthread_join(lwp2,NULL);
return 0;
}
#include
#include
#include
#include
void *func1(void *args)
{
int a = *(int *)args;
while(1)
{
printf("fun1中子线程%ld中参数为%d\n",pthread_self(), a);
a++;
sleep(1);
}
return NULL;
}
int main(int arg,char *args[])
{
pthread_t lwp1;
pthread_t lwp2;
int param1 = 1;
pthread_create(&lwp1, NULL,func1,¶m1);
pthread_create(&lwp2, NULL,func1,¶m1);
pthread_join(lwp1,NULL);
pthread_join(lwp2,NULL);
return 0;
}
#include
#include
#include
#include
typedef struct person
{
char name[32];
int age;
} PERSON;
void* func(void *args)
{
PERSON man = *(PERSON *)args;
while(1)
{
printf("我的名字叫%s\n",man.name);
printf("我的年龄是%d\n",man.age);
sleep(1);
}
return NULL;
}
int main(int arg,char *args[])
{
pthread_t lwp1;
PERSON person = {"tony",39};
pthread_create(&lwp1, NULL, func, &person);
pthread_join(lwp1,NULL);
return 0;
}
会阻塞直到回收了要回收的线程
#include
int pthread_join(pthread_t thread, void **retval);
等待线程结束(此函数会阻塞),并回收线程资源,类似进程的wait()函数。如果线程已经结束,那么该函数会立刻返回
thread:被等待的线程号
retval:用来存储线程退出状态的指针的地址
成功:0
失败:非0
不会阻塞,回收线程资源的任务交给了系统;
一般情况下,线程终止后,其终止状态一直保留到其他线程调用pthread_join获取他的状态为止。但是线程也可以被置为detach状态,这样的线程一旦终止就立刻回收他占用的所有资源,而不保留终止状态。不能对一个已经处于detach状态的线程调用pthread_join,这样的调用将返回EINVAL错误。也就是说,如果已经对一个线程调用了pthread_detach就不能再调用pthread_join了。
#include
int pthread_detach(pthread_t thread);
使调用线程与当前进程分离,分离后不代表线程不依赖于当前进程,线程分离的目的是将线程资源的回收工作交由系统自动完成,也就是说当被分离的线程结束后,系统会自动回收他的资源。所以,此函数不会阻塞。
thread:线程号
成功:0
失败:非0
#include
#include
#include
#include
#include
void *func1(void *args)
{
int a = *(int *)args;
while(1)
{
printf("func1中子线程%ld中参数为%d\n",pthread_self(), a);
sleep(1);
}
return NULL;
}
void *func2(void *args)
{
char name[32] = "";
memcpy(name, (char *)args ,strlen((char *)args));
while(1)
{
printf("func2中子线程%ld中参数为%s\n",pthread_self(), name);
sleep(1);
}
return NULL;
}
int main(int arg,char *args[])
{
pthread_t lwp1;
pthread_t lwp2;
int param1 = 1;
char param2[32] = "tony";
int t1, t2, t3, t4;
t1 = pthread_create(&lwp1, NULL,func1,¶m1);
printf("t1=%d\n", t1);
t2 = pthread_create(&lwp2, NULL,func2,param2);
printf("t2=%d\n", t2);
t3 = pthread_detach(lwp1);
printf("t3=%d\n", t3);
t4 = pthread_detach(lwp2);
printf("t4=%d\n", t4);
getchar();
return 0;
}
在进程中我们可以调用exit或_exit函数来结束进程,在一个线程中我们可以通过以下三种方式在不终止整个进程的情况下停止线程的控制流。线程从执行函数中返回;线程调用pthread_exit退出线程;线程可以被同一进程中的其他线程取消。
#include
void pthread_exit(void *retval);
退出调用线程。一个进程中的多个线程是共享该进程的数据段,因此,通常线程退出后所占用的资源并不会释放
#include
#include
#include
#include
#include
void *func(void *args)
{
int a = *(int *)args;
while(1)
{
printf("func中子线程%ld中参数为%d\n",pthread_self(), a);
a++;
if(a==5)
pthread_exit(NULL);
sleep(1);
}
return NULL;
}
int main(int arg,char *args[])
{
pthread_t lwp1;
pthread_t lwp2;
int param1 = 1;
pthread_create(&lwp1, NULL,func,¶m1);
pthread_create(&lwp2, NULL,func,¶m1);
pthread_detach(lwp1);
pthread_join(lwp2,NULL);
return 0;
}
可以取消自己;也可以取消当前进程中的其他线程;
#include
int pthread_cancel(pthread_t thread);
杀死(取消)线程
thread:要取消的线程id
成功:0
失败:出错编号
线程的取消并不是实时的,而是有一定的延时,需要等待线程到达某个取消点(检查点)。类似于玩游戏存档,必须到达指定的场所才能存储进度。杀死线程也不是立刻就能完成,必须要到达取消点。取消点:是线程检查是否被取消,并按请求进行动作的一个位置。通常是一些系统调用creat,open,close,read等。
#include
#include
#include
#include
void * pthread_fun01(void * arg)
{
int i = 0;
while(1)
{
printf("%s---------i=%d\n", (char *)arg, i++);
sleep(1); //取消点
}
return NULL;
}
int main(int arg,char *args[])
{
// 创建线程
pthread_t tid1;
pthread_create(&tid1, NULL, pthread_fun01, "任务A");
//线程分离
pthread_detach(tid1);
printf("5秒后结束任务A\n");
sleep(5);
pthread_cancel(tid1);
getchar();
return 0;
}
Linux下线程的属性是可以根据实际项目需要,进行设置,之前我们讨论的线程都是采用默认线程属性来创建的,默认属性已经可以解决绝大多数开发遇到的问题了。如果我们对程序的性能提出更高要求那么需要设置线程属性,比如可以通过设置线程栈的大小来降低内存的使用,增加最大线程个数
typedef struct
{
int etachstate; //线程的分离状态
int schedpolicy; //线程调度策略
struct sched_param schedparam; //线程的调度参数
int inheritsched; //线程的继承性
int scope; //线程的作用域
size_t guardsize; //线程栈末尾的警戒缓冲区大小
int stackaddr_set; //线程的栈设置
void* stackaddr; //线程栈的位置
size_t stacksize; //线程栈的大小
} pthread_attr_t;
线程属性初始化函数为pthread_attr_init, 这个函数必须在pthread_create函数之前调用。之后需要用pthread_attr_destroy函数来释放资源。线程属性主要包括如下属性:作用域、栈尺寸、栈地址、优先级、分离状态、调度策略和参数。默认的属性为非绑定、非分离、缺省的堆栈、与父进程同样级别的优先级。
注意:应先初始化线程属性,再pthread_create创建线程
初始化线程属性函数:int pthread_attr_init(pthread_attr_t *attr);
返回值:成功:0;失败:错误号
int pthread_attr_destroy(pthread_attr_t *attr);
返回值:成功:0;失败:错误号
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
参数:
attr:已经初始化的线程属性
detachstate:分离状态 PTHREAD_CREATE_DETACHED(分离线程)
PTHREAD_CREATE_JOINABLE(非分离线程)
注意:如果设置一个线程为分离线程,而这个线程运行又非常快,他很可能在pthread_create函数返回之前就终止了,他终止以后就可能将线程号和系统资源移交给其他线程使用,这样调用pthread_create的线程就得到了错误的线程号。要避免这种情况可以采用一定的同步措施,最简单的方法之一是可以在被创建的线程里调用pthread_cond_timedwait函数,让这个线程等待一会,留出足够的时间让函数pthread_create返回。设置一段等待时间,是在多线程编程里常用的方法。
int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstat);
参数:
attr:已经初始化的线程属性
detachstate:分离状态 PTHREAD_CREATE_DETACHED(分离线程)
PTHREAD_CREATE_JOINABLE(非分离线程)
当进程栈地址空间不够时,指定新建线程使用由malloc分配的堆空间作为栈空间。
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);
返回值:成功:0;失败:错误号
int pthread_attr_getstack(pthread_attr_t *attr, void **stackaddr, size_t *stacksize);
参数:
attr:指向一个线程属性的指针
stackaddr:返回获取的栈地址
stacksize:返回获取的栈大小
返回值:成功:0;失败:错误号
当系统中有很多线程时,可能需要减小每个线程栈的默认大小,防止进程的地址空间不够用,当线程调用的函数会分配很大的局部变量或者函数调用层次很深时,可能需要增大线程栈的默认大小。
int pthead_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
参数:
attr:指向一个线程属性的指针
stacksize:返回线程的堆栈大小
返回值:成功0;失败:错误号
int pthead_attr_getstacksize(pthread_attr_t *attr, size_t *stacksize);
参数:
attr:指向一个线程属性的指针
stacksize:返回线程的堆栈大小
返回值:成功0;失败:错误号
#include
#include
#include
#include
#include
#define SIZE 0x100000
void *th_fun(void *arg)
{
while(1)
sleep(1);
}
int main(int arg,char *args[])
{
pthread_t tid;
int err, detachstate, i=1;
pthread_attr_t attr;
size_t stacksize;
void *stackaddr;
pthread_attr_init(&attr);
pthread_attr_getstack(&attr, &stackaddr, &stacksize);
pthread_attr_getdetachstate(&attr, &detachstate);
if(detachstate == PTHREAD_CREATE_DETACHED)
printf("thread detached\n");
else if(detachstate == PTHREAD_CREATE_JOINABLE)
printf("thread join\n");
else
printf("thread unknown\n");
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
while(1)
{
stackaddr = malloc(SIZE);
if(stackaddr == NULL)
{
perror("malloc");
exit(1);
}
stacksize = SIZE;
pthread_attr_setstack(&attr, stackaddr, stacksize);
err = pthread_create(&tid, &attr, th_fun, NULL);
if(err != 0)
{
printf("err=%d\n", err);
printf("%s\n", strerror(err));
exit(1);
}
printf("%d\n", i++);
}
pthread_attr_destroy(&attr);
return 0;
}
#include
#include
#include
#include
#include
typedef struct
{
int time;
char task_name[32];
} MSG;
void *deal_fun(void *arg)
{
MSG msg = *(MSG *)arg;
int i = 0;
for(i=msg.time; i>0; i--)
{
printf("%s剩余时间%d\n", msg.task_name, i);
sleep(1);
}
return NULL;
}
int main(int arg,char *args[])
{
while(1)
{
MSG msg;
printf("输入新增的任务名:");
fgets(msg.task_name, sizeof(msg.task_name), stdin);
msg.task_name[strlen(msg.task_name)-1]=0;
printf("输入运行时间:");
scanf("%d", &msg.time);
getchar();//获取换行符
pthread_t tid;
pthread_create(&tid, NULL, deal_fun, (void *)&msg);
pthread_detach(tid);//线程分离
}
return 0;
}