进程是系统中程序执行和资源分配的基本单位。每个进程都拥有自己的数据段、代码段和堆栈段,这就造成了进程在进行切换等操作时都需要有比较负责的上下文切换等动作。

线程 :它是一个进程内的基本调度单位,有独立的程序计数器 堆栈 一系列的寄存器

线程按照其调度者可以分为用户级线程和核心级线程两种。一个用户级线程可以对应一个或几个核心级线程,也就是“一对一”或“多对一”模型。


1 创建线程
pthread_t a_thread; //保存线程变量
pthread_attr_t a_thread_attribute;//线程属性
void thread_function(void *argument);//线程执行时的函数
char *some_augument;//函数的参数

pthread_create(&a_thread,a_thread_attribute,(void*)&thread_function,(void*)some_augument);
线程属性只指明了需要使用的最小的堆栈大小。在以后的程序中,线程的属性可以指定其他的值,但现在大部分的程序可以使用缺省值。

#include<stdio.h>
#include<pthread.h>
void print_message_function(void *ptr)
{
    char *message;
    message=(char*)ptr;
    printf("%s",message);
}

int main()
{
    pthread_t pthread1,pthread2;
    char *message1="hello";
    char *message2="world";  
    pthread_create(&pthread1,NULL,(void*)&print_message_function,(void*)message1);
    pthread_create(&pthread2,NULL,(void*)&print_message_function,(void*)message2);
    return 0;
}
gcc -o pthread pthread.c -l pthread

当父进程结束时,所有的子线程都结束掉。
任何线程 (不论是父线程或者子线程 )调用exit 都会终止所有其他线程。如果希望线程分别终止,可以使用 pthread_exit函数。

当线程调用 s l e e p时,整个的进程都处于睡眠状态,也就是说,所有的三个线程都进入睡眠状态。

希望使一个线程睡眠的函数是 pthread_delay_np。例如让一个线程睡眠 2秒钟,用如下程序:
   struct timespec delay;
   delay.tv_sec = 2;
   delay.tv_nsec = 0;
   pthread_delay_np( &delay );

void pthread_exit(void *retval);
retval:pthread_exit()调用线程的返回值,可由其他函数如pthread_join来检查获取



2 修改线程的属性
帮定属性、分离属性、堆栈地址、堆栈大小、优先级
默认的:非绑定、非分离、缺省IM的堆栈、与父进程同等级别的优先级。

绑定属性:
linux中一个用户线程对应一个内核线程。绑定属性就是指一个用户线程固定地分配给一个内核线程,因为CPU的时间片的调度是面向内核进程的。

分离属性:
决定一个线程以什么样的方式来终止自己。
非分离情况下:线程结束时,所占用的系统资源没有被释放,只有当pthread_join()函数返回时,创建的线程才能释放自己占有的系统资源。

pthread_attr_init
pthread_attr_setscope//设置绑定属性
pthread_attr_setdetachstate//设置线程分离属性
pthread_attr_getschedparam//获取线程优先级
pthread_attr_setschedparam//设置线程优先级

int pthread_attr_init(pthread_attr_r *attr)
attr:线程属性

int pthread_attr_setscope(pthread_attr_r *attr,int scope)
scope:PTHREAD_SCOPE_SYSTEM:绑定
      PTHREAD_SCOPE_PROCESS:非绑定

int pthread_attr_setdetachstate(pthread_attr_r *attr,int detachstate)
detachstate:PTHREAD_CREATE_DETACHED:分离
            PTHREAD_CREATE_JOINABLE:非分离

int pthread_attr_getschedparam (pthread_attr_t *attr, struct sched_param *param)
param:线程优先级

int pthread_attr_setschedparam (pthread_attr_t *attr, struct sched_param *param)

3 线程控制

POSIX提供两种线程同步的方法, mutex和条件变量。
mutex是一种简单的加锁的方法来控制对共享资源的存取。

int pthread_jon(pthread_t th,void **thread_return );

thread_return:用户定义的指针,用来存储被等待线程的返回值.

pthread_join(pthread1,NULL);


a mutex互斥锁线程控制
mutex 是一种简单的加锁的方法来控制对共享资源的存取。这个互斥锁只有两种状态,也就是上锁和解锁,可以把互斥锁看作某种意义上的全局变量。

互斥锁的操作主要包括以下几个步骤。
• 互斥锁初始化:pthread_mutex_init
• 互斥锁上锁:pthread_mutex_lock
• 互斥锁判断上锁:pthread_mutex_trylock
• 互斥锁解锁:pthread_mutex_unlock
• 消除互斥锁:pthread_mutex_destroy
  其中,互斥锁可以分为快速互斥锁、递归互斥锁和检错互斥锁。这三种锁的区别主要在于其他未占有互斥锁的线程在希望得到互斥锁时的是否需要阻塞等待。快速锁是指调用线程会阻塞直至拥有互斥锁的线程解锁为止。递归互斥锁能够成功地返回并且增加调用线程在互斥上加锁的次数,而检错互斥锁则为快速互斥锁的非阻塞版本,它会立即返回并返回一个错误信息。

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
mutexattr:
PTHREAD_MUTEX_INITIALIZER:创建快速互斥锁
PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP:创建递归互斥锁                                                        
PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP:创建检错互斥锁

int pthread_mutex_lock(pthread_mutex_t *mutex,)
int pthread_mutex_trylock(pthread_mutex_t *mutex)
int pthread_mutex_unlock(pthread_mutex_t *mutex,)
int pthread_mutex_destroy(pthread_mutex_t *mutex,)

b 信号量线程控制

PV操作   P减一  V增一
进程(或线程)根据信号量的值来判断是否对公共资源具有访问权限。当信号量sem 的值大于等于零时,该进程(或线程)具有公共资源的访问权限;相反,当信号量 sem的值小于零时,该进程(或线程)就将阻塞直到信号量 sem 的值大于等于 0 为止。

PV 原语主要用于进程或线程间的同步和互斥这两种典型情况。若用于互斥,几个进程(或线程)往往只设置一个信号量 sem,它们的操作流程如图 9.2 所示。   当信号量用于同步操作时,往往会设置多个信号量,并安排不同的初始值来实现它们之 间的顺序执行。

  • sem_init 用于创建一个信号量,并能初始化它的值。
  • sem_wait 和 sem_trywait 相当于 P 操作,它们都能将信号量的值减一,两者的区别在于若信号量小于零时,sem_wait 将会阻塞进程,而 sem_trywait 则会立即返回。
  • sem_post 相当于 V 操作,它将信号量的值加一同时发出信号唤醒等待的进程。
  • sem_getvalue 用于得到信号量的值。
  • sem_destroy 用于删除信号量。

int sem_init(sem_t *sem,int pshared,unsigned int value)
sem:信号量
pshared:决定信号量能否在几个进程间共享。由于目前 Linux 还没有实现进程间共享信号量,所以这个值只能够取 0
value:信号量初始化值

     int sem_wait(sem_t *sem)
     int sem_trywait(sem_t *sem)
     int sem_post(sem_t *sem)
     int sem_getvalue(sem_t *sem)

     int sem_destroy(sem_t *sem)

 POSIX线程,也称为pthread
一 什么是线程
线程是在共享内存空间中并发的多道执行路径,他们共享一个进程的资源,如文件描述符和信号处理。
在两个普通的进程间进行切换时,内核准备从一个进程的上下文切换到另一个进程的上下文需要很大的开销,这里上下文切换的主要任务是保存老进程的CPU状态并加载新进程的保存状态,用新进程的内存映象替换老进程的内存映象。线程允许进程在几个正在运行的人物之间进行切换,而不必执行前面提到的上下文切换。

二 _clone函数调用
#include<sched.h>
int _clone(int (*fn)(void *fnarg),void *child_stack,int flags,void *arg);
_clone的目的是为了更容易实现pthread库

pthread_create函数创建线程

(*fn)(void *fnarg) 是函数指针,当执行子进程时调用这个函数,fnarg是传递给fn的参数
child_stack是只想为你子进程分配的堆栈的指针
arg被传递给子函数

返回值为创建的子进程的进程ID

二 pthread库
1 pthread是什么
pthread是一种标准化模型,用于把一个程序分成一组能够同时执行的任务。
pthread是实现POSIX线程标准的C函数调用和数据结构、提供接口的头文件<pthread.h>,以及让调用起作用的库libpthread.o组成的集合

2 pthread_create函数
#include <pthread.h>
int pthread_create(pthread_t *thread,pthread_attr_t *attr,void *(*start_routime)(void *),void *arg);
thread:心线程的标识符
attr:决定了对线程应用哪种线程属性
pthread_attr_init设置新线程的属性
*start_routime是一个指向新线程中要执行的函数的指针
arg 传递给*start_routime的参数。
成功返回0,并在thread中保存新线程的标识符。
与fork()调用创建一个进程的方法不同,pthread_create()创建的线程并不具备与主线程(即调用pthread_create()的线程)同样的执行序列,而是使其运行start_routine(arg)函数。thread返回创建的线程ID,而attr是创建线程时设置的线程属性(见下)。pthread_create()的返回值表示线程创建是否成功。尽管arg是void *类型的变量,但它同样可以作为任意类型的参数传给start_routine()函数;同时,start_routine()可以返回一个void *类型的返回值,而这个返回值也可以是其他类型,并由pthread_join()获取。

pthread_t在头文件/usr/include/bits/pthreadtypes.h中定义:typedef unsigned long int pthread_t;

2 pthread_exit函数
pthread_exit函数使用函数pthread_cleanup_push调用任何用于该线程的清楚处理函数,然后终止当前线程的执行,返回retval。retval可以由父进程或其他进程通过pthread_join来检索。
#include<pthread.h>
void pthread_exit(void *retval);

3 pthread_join函数
用于挂起当前进程,直到th指向的进程终止为止.
第一个参数为被等待的线程标识符,第二个参数为一个用户定义的指针,它可以用来存储被等待线程的返回值。这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。一个线程的结束有两种途径,一种是象我们上面的例子一样,函数结束了,调用它的线程也就结束了;另一种方式是通过函数pthread_exit来实现。
#include<pthread.h>
int pthread_join(pthread_t th,void **thread_return);
int pthread_detach(pthread_t th);

4 pthread_atfork函数
登记了三个处理函数,在创建一个新线程的某些时候被调用
int pthreadd_atfork(void (*prepare)(void),void(*parent)(void),void(*child)(void));

5 取消线程
int pthread_cancel(pthread_t thread);
int pthread_setcancelstate(int state,int *oldstate);
int pthread_setcanceltype(int type,int *oldtype);
void pthread_testcancel(void);

一个线程可以使用pthread_setcancelstate来设置它的取消状态,这个函数有两个参数,satate是新状态,oldstate是变量指针。
state:
PTHREAD_CANCEL_ENABLE,允许请求取消
PTHREAD_CANCEL_DISABLE忽略取消请求

pthread_setcanceltype改变一个线程对取消请求的相应方式,
type:
PTHREAD_CANCEL_ASYNCHRONOUS 线程会被立即取消
PTHREAD_CANCEL_DEFERRED 延迟取消线程直到达到一个取消点

取消点通过pthread_testcancel建立

6 pthread cleanup宏
登记了一个处理函数routine,当调用pthread_exit终止线程或者线程允许取消请求而同时又到达一个取消点时,就用arg指向的空指针调用这个处理函数。

#include<pthread.h>
void pthread_cleanup_push(void(*routine),(void *),void *arg);
void pthread_cleanup_pop(int execute);
void pthread_cleanup_push_defer_np(void(*routine),(void *),void *arg);
void pthread_cleanup_pop_restore_np(int execute);
处理函数被压入到一个栈中,pthread_cleanup_pop取消对最近入栈的清除处理函数的登记。

7 创建绑定进程
#include <pthread.h>
pthread_attr_t attr;
pthread_t tid;

/*初始化属性值,均设为默认值*/
pthread_attr_init(&attr);
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
第二个参数有两种取值:PTHREAD_SCOPE_SYSTEM,PTHREAD_SCOPE_PROCESS
pthread_create(&tid, &attr, (void *) my_function, NULL);

8 线程的分离状态决定一个线程以什么样的方式来终止自己。在上面的例子中,我们采用了线程的默认属性,即为非分离状态。而分离线程不是这样子的,它没有被其他的线 程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。
设置线程分离状态的函数为 pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)。
第二个参数可选为PTHREAD_CREATE_DETACHED(分离线程)和 PTHREAD _CREATE_JOINABLE(非分离线程)

9 优先级
另外一个可能常用的属性是线程的优先级,它存放在结构sched_param中。用函数pthread_attr_getschedparam和函数 pthread_attr_setschedparam进行存放
#include <pthread.h>
#include <sched.h>
pthread_attr_t attr;
pthread_t tid;
sched_param param;
int newprio=20;

pthread_attr_init(&attr);
pthread_attr_getschedparam(&attr, &param);
param.sched_priority=newprio;
pthread_attr_setschedparam(&attr, &param);
pthread_create(&tid, &attr, (void *)myfunction, myarg);

 

 posted on 2009-07-01 17:03  清水湾  阅读(585)  评论(0)    收藏  举报