1. 创建一个线程
1.1 创建线程练习
线程实际上是应用层的概念,在 Linux 内核中,所有的调度实体都被称为任务 (task) ,
他们之间的区别是:有些任务自己拥有一套完整的资源,而有些任务彼此之间共享一套资源
对此函数的使用需要知道以下几点:
1,线程例程指的是:如果线程创建成功,那么该线程会立即去执行的函数。
2,POSIX 线程库的所有 API 对返回值的处理原则都是一致的:成功返回 0,失败返回 错误码 errno。
3 ,线程属性如果为 NULL,则会创建一个标准属性的线程,线程的属性非常多,下面 是关于线程属性的详细讨论。
跟线程属性有关的 API 有:
thread1.c
#include <stdio.h>
#include <pthread.h>
//回调函数
void *xiancheng (void *argc)
{
int i=0;
while (1)
{
printf("%d\n",i);
i++;
sleep(1);
}
}
int main()
{
pthread_t tid;
//创建一个线程
pthread_create(&tid,NULL,&xiancheng,NULL);
pause();//停留在此
}
成功
1.2 退出线程
线程跟进程类似,在缺省的状态下退出之后,会变成僵尸线程,并且保留退出值。其他 线程可以通过相关 API 接合该线程——使其资源被系统回收,如果愿意的话还可以顺便获取 其退出值。下面是相关 API:
1.3 接合指定线程
用上述函数需要注意以下几点:
1,如果线程退出时没有退出值,那么 retval 可以指定为 NULL。
2,pthread_join( )指定的线程如果尚在运行,那么他将会阻塞等待。
3,pthread_tryjoin_np( )指定的线程如果尚在运行,那么他将会立即出错返回。
1.4 取消线程请求
另外,或许在某个时刻不能等某个线程“自然死亡”,而需要勒令其马上结束,此时可 以给线程发送一个取消请求,让其中断执行而退出。用到如下 API:
而当线程收到一个取消请求时,他将会如何表现取决于两个东西:一是当前的取消状态, 二是当前的取消类型。线程的取消状态很简单——分别是 PTHREAD_CANCEL_ENABLE 和 PTHREAD_CANCEL_DISABLE,前者是缺省的,代表线程可以接受取消请求,后者代表关闭 取消请求,不对其响应。
而在线程接受取消请求的情况下,如何停下来又取决于两种不同的响应取消请求的策略 ——延时响应和立即响应,当采取延时策略时,线程并不会立即退出,而是要遇到所谓的“取 消点”之后,才退出。而“取消点”,指的是一系列指定的函数。
1.5 分离线程的使用细节
以上API 都是针对线程属性操作的,所谓线程属性是类型为 pthread_attr_t 的变量, 设置一个线程的属性时,通过以上相关的函数接口,将需要的属性添加到该类型变量里 面,再通过 pthread_create( )的第二个参数来创建相应属性的线程。
线程属性变量的使用步骤是:
1,定义线程属性变量,并且使用 pthread_attr_init( )初始化。
2,使用 pthread_attr_setXXX( )来设置相关的属性。
3,使用该线程属性变量创建相应的线程。
4,使用 pthread_attr_destroy( )销毁该线程属性变量。
线程的属性很多,其中着重关注的几个属性 API 是:
一条线程如果是可接合的,意味着这条线程在退出时不会自动释放自身资源,而会成为 僵尸线程,同时意味着该线程的退出值可以被其他线程获取。因此,如果不需要某条线程的 退出值的话,那么最好将线程设置为分离状态,以保证该线程不会成为僵尸线程。
分离线程在退出时,相关资源会被系统自动回收,而不需要显式调用 pthread_join 函数等待线程结束。
重点
主线程的结束,会导致整个程序的退出结束,那时候任何线程的任何操作都将停止,包括其他线程要输出但是还没来得及输出的情况
thread2.c
#include <stdio.h>
#include <pthread.h>
//分离线程的细节
//重点,主线程的结束,会导致整个程序的退出结束,那时候任何线程的任何操作都将停止,包括其他线程要输出但是还没来得及输出的情况
//回调函数
void *xiancheng (void *argc)
{
int i=0;
while (1)
{
sleep(1);
printf("%d\n",i);
i++;
if(i>10){
break;
}
}
//终止线程的函数。它的作用是终止当前线程的执行,并将一个退出状态传递给主线程(或者调用 pthread_join 等待的线程)。
//但是分离线程拿不到退出状态
pthread_exit("byebye\n");
}
int main()
{
printf("我创建线程啦\n");
pthread_t tid1;
pthread_t tid2;
void* p1 = NULL;
void* p2 = NULL;
//设置分离属性
//创建属性变量
pthread_attr_t attr1;
//初始化属性变量
pthread_attr_init(&attr1);
//对这个属性变量,设置分离属性
pthread_attr_setdetachstate(&attr1,PTHREAD_CREATE_DETACHED);
//把设置好的属性变量attr1,传到创建线程函数中
//创建一个线程
pthread_create(&tid1,&attr1,&xiancheng,NULL);//这个线程是分离属性的
pthread_create(&tid2,NULL,&xiancheng,NULL);//默认创建的线程是可结合的属性
pthread_join(tid1,&p1);//tid1是分离线程,主线程不需要等待它的执行
pthread_join(tid2,&p2);//相当于wait,对于分分离线程它会一直等待子线程执行完成
printf("p1:%s\n",(char*)p1);
printf("p2:%s\n",(char*)p2);
}
2. 设置线程调度策略
2.1 nice函数
动态优先级数值越大,优先级越低
nice.c
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
//nice函数
void *a1(void *arg)
{
char *msg = (char *)arg;//把参数解出来,需要什么类型,按情况解参数
while(1)
{
if(strcmp(msg,"A\0") == 0)
{
nice(-19);//给A的亲和度降到最低,cpu会打印他,因为亲和度低
}
if(strcmp(msg,"B\0") == 0)
{
nice(15);//给B的亲和度升到最高,cpu很少打印他,因为亲和度太高了
}
fprintf(stderr, "%c", *msg);
}
}
//分离与接合
int main()
{
//定义一个线程号tid
pthread_t tid1;
pthread_t tid2;
pthread_create(&tid1, NULL, a1, "A");//屌丝线程,静态优先级为0
pthread_create(&tid2, NULL, a1, "B");//屌丝线程,静态优先级为0
pause();
}
2.2 静态优先级和调度策略解析
2.2.1 获取、设置线程是否继承创建者的调度策略
2.2.2 获取、设置线程的调度策略
当需要给一个线程设置调度方面的属性时 ,必须先将线程的 inheritsched 设置为 PTHREAD_EXPLICIT_SCHED。
SCHED _FIFO 调度策略为此,通俗讲就是,获得cpu就会一直执行自己直到自己运行结束,才会让出cpu
SCHED RR 调度策略为此,通俗讲就是 ,每个线程都会获得cpu的时间在获得时间内执行,时间到了换下一个拥有时间的线程执行
前面两个策略有个条件,就是和同批次竞争者的静态优先级一样,如果有的竞争者静态优先级高过它,那就让这个静态优先级高的执行(老师说的高富帅)(就是如果他们的静态优先级一样的话来博弈。)
当线程的调度策略为 SCHED_OTHER 时,其静态优先级 (static priority) 必须设置 为 0 。该调度策略是 Linux 系统调度的默认策略,处于 0 优先级别的这些线程按照所谓的动 态优先级被调度,而动态优先级起始于线程的 nice 值,且每当一个线程已处于就绪态但被 调度器调度无视时,其动态优先级会自动增加一个单位,这样能保证这些线程竞争 CPU 的 公平性。
2.2.3 获取、设置线程静态优先级
1,静态优先级是一个定义如下的结构体:
struct sched_param
{
int sched_priority;
};
set_policy.c
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
//静态优先级和调度策略的探讨
void *a1(void *arg)
{
char *msg = (char *)arg;//把参数解出来,需要什么类型,按情况解参数
while(1)
{
fprintf(stderr, "%c", *msg);
}
}
//分离与接合
int main()
{
//定义一个线程号tid
pthread_t tid1;
pthread_t tid2;
pthread_attr_t attr1;
pthread_attr_t attr2;
//初始化属性变量
pthread_attr_init(&attr1);
pthread_attr_init(&attr2);
//选择是否继承创建者的调度策略
pthread_attr_setinheritsched(&attr1,PTHREAD_EXPLICIT_SCHED);
pthread_attr_setinheritsched(&attr2,PTHREAD_EXPLICIT_SCHED);
//设置线程的调度策略
// pthread_attr_setschedpolicy(&attr1, SCHED_FIFO);
// pthread_attr_setschedpolicy(&attr2, SCHED_FIFO);
pthread_attr_setschedpolicy(&attr1, SCHED_RR);
pthread_attr_setschedpolicy(&attr2, SCHED_RR);
struct sched_param policy;
policy.sched_priority = 20;
//设置静态优先级
pthread_attr_setschedparam(&attr1,&policy);//只有管理员才能修改线程的静态优先级
// policy.sched_priority = 21;
pthread_attr_setschedparam(&attr2,&policy);
pthread_create(&tid1, &attr1, a1, "A");//屌丝线程,静态优先级为0
pthread_create(&tid2, &attr2, a1, "B");//屌丝线程,静态优先级为0
pause();
}