Linux线程

线程原语

pthread_create
创建线程

#include <pthread.h>

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,

                                void *(*start_routine) (void *), void *arg);


pthread_t *thread  传出参数,返回线程tid
const pthread_attr_t *attr  设置线程属性,例如:线程用户栈大小,线程优先级
void *(*start_routine) (void *) 线程的处理动作 函数指针void*fun(void*)
void *arg 线程处理动作的参数

pthread_self
获取调用线程tid

#include <pthread.h>

pthread_t pthread_self(void);

  思考:pthread_self与tid等价吗?

      不等价,应为pthread_self在线程中获取tid(线程当前存在)

      tid是主控线程的变量在用户栈, 子线程看不到, 所以是主控线程获取输出的   输出时可能值一致(但可能此线程已经被释放,回收)

  如果任意一个线程调用了exit或_exit,则整个进程的所有线程都终止,由于从main函数return也相当于调用exit,为了防止新创建的线程还没有得到执行就终止,我们在main函数return之前延时1秒,这只是一种权宜之计。

pthread_exit

线程退出函数

调用线程退出函数,注意和exit函数的区别,任何线程里exit导致进程退出,其他线程未工作结束,主控线程退出时不能return或exit。
需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。

#include <pthread.h>

void pthread_exit(void *retval);

void *retval:线程退出时传递出的参数,可以是退出值或地址,如是地址时,不能是线程内部申请的局部地址。

 

pthread_join

等待线程退出函数
#include <pthread.h>

int pthread_join(pthread_t thread, void **retval);

pthread_t thread:回收线程的tid


void **retval:接收退出线程传递出的返回值

返回值:成功返回0,失败返回错误号

 

调用该函数的线程将挂起等待,直到id为thread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的,总结如下:

如果thread线程通过return返回,retval所指向的单元里存放的是thread线程函数的返回值。

如果thread线程被别的线程调用pthread_cancel异常终止掉,retval所指向的单元里存放的是常数PTHREAD_CANCELED。(即为-1)

如果thread线程是自己调用pthread_exit终止的,retval所指向的单元存放的是传给pthread_exit的参数。

如果对thread线程的终止状态不感兴趣,可以传NULL给retval参数。

 

pthread_cancel
在进程内某个线程可以取消另一个线程。

#include <pthread.h>

int pthread_cancel(pthread_t thread);


被取消的线程,退出值,定义在Linux的pthread库中常数PTHREAD_CANCELED的值是-1。可以在头文件pthread.h中找到它的定义:
#define PTHREAD_CANCELED ((void *) -1)

实例:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
void *thr_fn1(void *arg)
{
  printf("thread 1 returning\n");
  return (void *)1;
}
void *thr_fn2(void *arg)
{
  printf("thread 2 exiting\n");
  pthread_exit((void *)2);
}
void *thr_fn3(void *arg)
{
  while(1) {
    printf("thread 3 writing\n");
    sleep(1);
  }
}
int main(void)
{
  pthread_t tid;
  void *tret;
  pthread_create(&tid, NULL, thr_fn1, NULL);
  pthread_join(tid, &tret);
  printf("thread 1 exit code %d\n", (int)tret);
  pthread_create(&tid, NULL, thr_fn2, NULL);
  pthread_join(tid, &tret);
  printf("thread 2 exit code %d\n", (int)tret);
  pthread_create(&tid, NULL, thr_fn3, NULL);
  sleep(3);
  pthread_cancel(tid);
  pthread_join(tid, &tret);
  printf("thread 3 exit code %d\n", (int)tret);
  return 0;
}

结果:退出码分别是1,2,-1.

对于线程3:如果线程中只有while(1),那么pthread_cancle则不能终止线程。如果加了sleep(1);则可以终止线程。

总结:signal何时终止信号?(中断,异常,系统调用)

     cancle何时事件何时处理?(系统调用)

   就是说线程没有系统调用则不能使用cancle结束,所以我们可以用pthread_testcancle()来产生一次空调用,该函数的作用就是,产生一次系统调用检测终止事件。

线程终止方式

如果需要只终止某个线程而不终止整个进程,可以有三种方法:

1.从线程主函数return。这种方法对主控线程不适用,从main函数return相当于调用exit。

2.一个线程可以调用pthread_cancel终止同一进程中的另一个线程。

3.线程可以调用pthread_exit终止自己。

同一进程的线程间,pthread_cancel向另一线程发终止信号。系统并不会马上关闭被取消线程,只有在被取消线程下次系统调用时,才会真正结束线程。或调用pthread_testcancel,让内核去检测是否需要取消当前线程.

 

pthread_detach

将线程置为分离态
#include <pthread.h>
int pthread_detach(pthread_t tid);


pthread_t tid:分离线程tid

返回值:成功返回0,失败返回错误号。

一般情况下,线程终止后,其终止状态一直保留到其它线程调用pthread_join获取它的状态为止。但是线程也可以被置为detach状态,这样的线程一旦终止就立刻回收它占用的所有资源,而不保留终止状态。不能对一个已经处于detach状态的线程调用pthread_join,这样的调用将返回EINVAL。如果已经对一个线程调用了pthread_detach就不能再调用pthread_join了。同理,如果对一个已经处于join回收状态的线程调用depatch设置分离,也会失败。

 

线程属性

 

线程属性初始化

先初始化线程属性,再pthread_create创建线程

#include <pthread.h>
int pthread_attr_init(pthread_attr_t *attr); //初始化线程属性
int pthread_attr_destroy(pthread_attr_t *attr); //销毁线程属性所占用的资源

线程分离状态的函数:

#include <pthread.h>
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
//设置线程属性,分离or非分离

int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate); //获取程属性,分离or非分离

pthread_attr_t *attr:被已初始化的线程属性

int *detachstate:可选为PTHREAD_CREATE_DETACHED(分离线程)和PTHREAD _CREATE_JOINABLE(非分离线程)这里要注意的一点是,如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在pthread_create函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用pthread_create的线程就得到了错误的线程号。

要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用pthread_cond_timedwait函数,让这个线程等待一会儿,留出足够的时间让函数pthread_create返回。设置一段等待时间,是在多线程编程里常用的方法。但是注意不要使用诸如wait()之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题。

NPTL


1.察看当前pthread库版本
getconf GNU_LIBPTHREAD_VERSION

2.NPTL实现机制(POSIX),Native POSIX Thread Library

3.使用线程库时gcc指定-lpthread

细节注意
1.主线程退出其他线程不退出,主线程应调用ptrhed_exit
2.避免僵线程
pthread_join
pthread_deatch
pthread_create指定分离属性 被join线程 可能在join函数返回前就释放完自己的所有内存资源,所以不应当返回被回收线程栈中的值;
3.malloc和mmap申请的内存可以被其他线程释放
4.如果线程终止时没有释放加锁的互斥量,则该互斥量不能再被使用
5.应避免在多线程模型中调用fork除非,马上exec,子进程中只有调用fork的线程存在,其他线程在子进程中均pthread_exit
6.信号的复杂语义很难和多线程共存,应避免在多线程引入信号机制

 

posted @ 2018-07-29 13:35  Lune-Qiu  阅读(213)  评论(0编辑  收藏  举报