ipmsg学习笔记3—多线程1

来源:互联网 发布:埃隆马斯克 知乎 编辑:程序博客网 时间:2024/05/16 23:37
Linux下的多线程遵循POSIX接口,称为pthread。需要pthread.h头文件。
1、创建线程的函数
int pthread_create(pthread_t *restrict tidp,
const pthread_attr_t *restrict attr,
void *(*start_rtn)(void),
void *restrict arg);
若成功则返回0,错误则返回响应的错误码。常见的错误返回代码为EAGAIN和EINVAL,前者表示系统限制创建新的线程,例如线程数目过多了;后者表示第二个参数代表的线程属性值非法。
第一个参数:线程ID,
第二个参数:线程属性,
第三个参数:线程处理函数,
第四个参数:处理函数参数。

所有线程都有一个线程号,也就是Thread ID。其类型为pthread_t。通过调用pthread_self()函数可以获得自身的线程号。
其实在Linux中,新建的线程并不是在原先的进程中,而是系统通过一个系统调用clone()。该系统copy了一个和原先进程完全一样的进程,并在这个进程中执行线程函数。不过这个copy过程和fork不一样,copy后的进程和原先的进程共享了所有的变量,运行环境。这样,原先进程中的变量变动在copy后的进程中便能体现出来。

另外附带的说明一下,restrict的含义,restrict是C99引入的新关键字,这个关键字表明指针 x 是指向这块区域的唯一指针,也就是这块内存区域的唯一入口,任何修改这块内存单元的操作都需要通过指针 x 才能够完成,编译器可以对程序做些优化。


2、设置线程的属性
线程具有属性,用pthread_attr_t表示,在对该结构进行处理之前必须进行初始化,在使用后需要对其去除初始化。我们用pthread_attr_init函数对其初始化,用pthread_attr_destroy对其去除初始化。
线程属性结构如下(每个属性都有其相应的设置和获取函数)
typedef struct
{
int detachstate; 线程的分离状态
int schedpolicy; 线程调度策略
structsched_param schedparam; 线程的调度参数
int inheritsched; 线程的继承性
int scope; 线程的作用域
size_t guardsize; 线程栈末尾的警戒缓冲区大小
int stackaddr_set;
void* stackaddr; 线程栈的位置
size_t stacksize; 线程栈的大小
}pthread_attr_t;
函数原型如下,成功返回0,失败返回-1:
int pthread_attr_init(pthread_attr_t*attr);
int pthread_attr_destroy(pthread_attr_t*attr);

1)线程的分离状态
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int*detachstate);

int pthread_attr_setdetachstate(pthread_attr_t *attr, intdetachstate);

设置线程的分离状态为PTHREAD_CREATE_JOINABLE或PTHREAD_CREATE_DETACHED。

默认是joinable,有一些资源会在线程结束后仍然保持占用状态,直到另外的线程对这个线程使用pthread_join。detached线程不是这样子的,它没有被其他的线程所等待(join),自己运行结束了,线程也就终止了,马上释放系统资源。

若成功返回0,若失败返回-1。

2)线程调度策略和参数
int pthread_attr_getschedpolicy(const pthread_attr_t*attr,int *policy);
int pthread_attr_setschedpolicy(pthread_attr_t *attr,intpolicy);

调度策略可能的值是先进先出(SCHED_FIFO)、轮转法(SCHED_RR),或其它(SCHED_OTHER)。
SCHED_FIFO策略允许一个线程运行直到有更高优先级的线程准备好,或者直到它自愿阻塞自己。在SCHED_FIFO调度策略下,当有一个线程准备好时,除非有平等或更高优先级的线程已经在运行,否则它会很快开始执行。
SCHED_RR(轮循)策略是基本相同的,不同之处在于:如果有一个SCHED_RR策略的线程执行了超过一个固定的时期(时间片间隔)没有阻塞,而另外的SCHED_RR或SCHBD_FIPO策略的相同优先级的线程准备好时,运行的线程将被抢占以便准备好的线程可以执行。
当有SCHED_FIFO或SCHED_RR策赂的线程在一个条件变量上等持或等持加锁同一个互斥量时,它们将以优先级顺序被唤醒。即,如果一个低优先级的SCHED_FIFO线程和一个高优先织的SCHED_FIFO线程都继承性决定调度的参数是从创建的进程中继承还是使用在schedpolicy和schedparam属性中显式设置的调度信息。Pthreads不为inheritsched指定默认值,因此如果你关心线程的调度策略和参数,必须先设置该属性。
继承性的可能值是PTHREAD_INHERIT_SCHED(表示新现成将继承创建线程的调度策略和参数)和PTHREAD_EXPLICIT_SCHED(表示使用在schedpolicy和schedparam属性中显式设置的调度策略和参数)。
如果你需要显式的设置一个线程的调度策略或参数,那么你必须在设置之前将inheritsched属性设置为PTHREAD_EXPLICIT_SCHED.
下面我来讲进程的调度策略和调度参数。我会结合下面的函数给出本函数的程序例子。在等待锁相同的互斥且,则当互斥量被解锁时,高优先级线程将总是被首先解除阻塞。

int pthread_attr_getschedparam(const pthread_attr_t *restrict attr, struct sched_param *restrict param);
int pthread_attr_setschedparam(pthread_attr_t *restrict attr, const struct sched_param*restrict param);
struct sched_param
{
intsched_priority;
};

结构sched_param的子成员sched_priority控制一个优先权值,大的优先权值对应高的优先权。系统支持的最大和最小优先权值可以用sched_get_priority_max函数和sched_get_priority_min函数分别得到。

注意:如果不是编写实时程序,不建议修改线程的优先级。因为,调度策略是一件非常复杂的事情,如果不正确使用会导致程序错误,从而导致死锁等问题。如:在多线程应用程序中为线程设置不同的优先级别,有可能因为共享资源而导致优先级倒置。

3)线程继承机制
int pthread_attr_getinheritsched(const pthread_attr_t*attr,int *inheritsched);
int pthread_attr_setinheritsched(pthread_attr_t *attr,intinheritsched);
继承性决定调度的参数是从创建的进程中继承还是使用在schedpolicy和schedparam属性中显式设置的调度信息。Pthreads不为inheritsched指定默认值,因此如果你关心线程的调度策略和参数,必须先设置该属性。
继承性的可能值是PTHREAD_INHERIT_SCHED(表示新线程将继承创建线程的调度策略和参数)和PTHREAD_EXPLICIT_SCHED(表示使用在schedpolicy和schedparam属性中显式设置的调度策略和参数)。
如果你需要显式的设置一个线程的调度策略或参数,那么你必须在设置之前将inheritsched属性设置为PTHREAD_EXPLICIT_SCHED。

4)线程竞争范围
int pthread_attr_getscope(const pthread_attr_t *restrict attr, int *restrict contentionscope);
int pthread_attr_setscope(pthread_attr_t *attr, int contentionscope);

定义创建的线程的竞争范围为PTHREAD_SCOPE_SYSTEM、PTHREAD_SCOPE_PROCESS。Linux Threads只实现了PTHREAD_SCOPE_SYSTEM,这意味着它将和机器上运行的所有进程竞争CPU时间。标准指定的另外一个值,PTHREAD_SCOPE_PROCESS,表示竞争只存在于运行中的进程的线程之间:即,线程的优先级是相对于其它进程中的线程的优先级的,而不必考虑进程的优先级如何。LinuxThread不支持PTHREAD_SCOPE_PROCESS。

5)线程栈的大小
(1)

int pthread_attr_getstack(pthread_attr_t *attr,void **stackaddr,size_t*stacksize);

int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr,size_t stacksize);

设置线程栈的起始地址和栈大小。默认起始地址0,大小0x800000(8M,我的机子……)。

PS:使用get时往往显示的是addr = 0,size = 0,这可能是系统的BUG,size应给为8M而不是0。

(2)

上面的2个函数可以细化成4个:

int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr);

int pthread_attr_getstackaddr(pthread_attr_t *attr, void**stackaddr);

int pthread_attr_setstacksize(pthread_attr_t *attr, size_tstacksize);

int pthread_attr_getstacksize(pthread_attr_t *attr, size_t*stacksize);

这里get得到的size为8M,所以pthread_attr_getstack获得的size大小是不能说明栈的大小的。

6)栈溢出保护区大小
intpthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);
intpthread_attr_getguardsize(pthread_attr_t *attr, size_t *guardsize);

设置线程的栈溢出保护区大小,默认4096B,即4K。

原创粉丝点击