当前位置 : 主页 > 编程语言 > java >

pthread

来源:互联网 收集:自由互联 发布时间:2022-06-23
文章目录 ​​示例程序​​ ​​线程的合并与分离​​ ​​线程的合并​​ ​​线程的分离​​ ​​线程的属性​​ ​​绑定属性​​ ​​分离属性​​ ​​调度属性​​ ​​算


文章目录

  • ​​示例程序​​
  • ​​线程的合并与分离​​
  • ​​线程的合并​​
  • ​​线程的分离​​
  • ​​线程的属性​​
  • ​​绑定属性​​
  • ​​分离属性​​
  • ​​调度属性​​
  • ​​算法​​
  • ​​轮询​​
  • ​​先进先出​​
  • ​​其它​​
  • ​​优先级​​
  • ​​继承权​​
  • ​​堆栈大小属性​​
  • ​​满占警戒区大小属性​​

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);
  • 引用头文件:​​#include <pthread.h>​​
  • 编译链接:​​gcc your_program.o -o your_program -lpthread​​

参数解析:

  • ​​pthread_t *thread​​: 创建后返回的线程句柄;
  • ​​pthread_attr_t* attr​​​: 线程属性,可选。​​NULL​​;
  • ​​*(*start_routine)(void*)​​​: 线程入口函数,通过​​pthread_join()​​ 接口获得函数返回值;
  • ​​*arg​​: 线程入口函数的参数;

返回值:如果线程调用成功就会返回 0。

示例程序

#include <stdio.h>
#include <pthread.h>

void* thread(void* arg)
{
printf("This is a thread and arg = %d.\n", *(int*)arg);
*(int*)arg = 0;
return arg;
}

int main(int argc, char* argv[])
{
pthread_t th;
int ret;
int arg = 10;
int* thread_ret = NULL;
ret = pthread_create(&th, NULL, thread, &arg); // 创建了一个新的线程,线程的入口函数是 thread,传入了一个为 10 的参数。
if(ret != 0)
{
printf("Create thread error!\n");
return -1;
}
printf("This is the main process.\n");
pthread_join(th, (void**)&thread_ret); // 第一个参数是创建线程的句柄,第二个参数会接受线程的返回值。该函数会阻塞主进程的执行,直到合并的线程执行结束。
printf("thread_ret = %d.\n", *thread_ret);
return 0;
}

由于是多线程程序,在不同环境下会有输出的出入。
pthread_#include

线程的合并与分离

线程的合并

通过 ​​pthread_create()​​​ 创建了一个线程,对于该系统资源,需要进行资源回收——线程合并(​​pthread_join()​​​),会阻塞调用进程或线程,直到被合并的线程结束为止。当被合并的线程结束时,​​pthread_join()​​ 会回收这个线程的资源,并将这个线程的返回值返回。

线程的分离

​​pthread_detach()​​​,将线程资源的回收工作交由系统自动完成,当被分离的线程结束后,系统会自动回收它的资源。程序无法获得分离线程的返回值。​​pthread_detach(th);​​

线程的属性

线程的属性由一个线程属性对象来描述,由 ​​pthread_attr_init()​​​ 接口初始化,并由 ​​pthread_attr_destory()​​ 来销毁。

int pthread_attr_init(pthread_attr_t* attr);
int pthread_attr_destory(pthread_attr_t* attr);

绑定属性

轻进程(LWP)属于内核的调度实体,一个轻进程可以控制一个或多个线程。与普通任务相比, LWP 与其他进程共享所有(或大部分)的逻辑地址空间和系统资源,与线程相比,LWP 有它自己的进程标识符,并和其它进程有着父子关系。

线程既可以由应用程序管理,又可以由内核管理,而 LWP 仅由内核管理,并向普通进程一样被调度,就和 Linux 系统的内核线程一样。

默认情况下,对于一个拥有 n 个线程的进程,启动多少个轻进程,由哪些轻进程来控制哪些线程由操作系统决定(非绑定),如果要指定某个线程“绑”在某个轻进程上,就可以成为绑定。

绑定属性应用场景:被绑定的线程具有较高的响应速度,因为操作系统的调度主体是轻进程,绑定属性可以保证该线程在需要的时候总有一个轻进程可用。

设定绑定属性:​​int pthread_attr_setscope(pthread_attr_t* attr, int scope);​​ 第一个参数线程属性对象的指针,第二个参数绑定类型;

绑定类型:

  • ​​PTHREAD_SCOPE_SYSTEM​​ 绑定的;
  • ​​PTHREAD_SCOPE_PROCESS​​​ 非绑定的;Linux 的线程永远都是绑定的,所以​​PTHREAD_SCOPE_PROCESS​​在Linux中不管用,一切都是兼容的需要。
#include <stdio.h>
#include <pthread.h>
...
int main(int argc, char *argv[])
{
pthread_attr_t attr;
pthread_t th;
...
pthread_attr_init(&attr);
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
pthread_create(&th, &attr, thread, NULL);
...
}

分离属性

让线程在创建之前就确定是分离的,可以不再调用 pthread_join() 或者 pthread_detach() 来回收线程资源。

设置接口 ​​pthread_attr_setdetachstat(pthread_attr_t* attr, int detachstate);​​

detachstate:

  • ​​PTHREAD_CREATE_DETACHED​​: 分离的;
  • ​​PTHREAD_CREATE_JOINABLE​​: 可合并的,也是默认属性;
#include <stdio.h>
#include <pthread.h>
...
int main(int argc, char *argv[])
{
pthread_attr_t attr;
pthread_t th;
...
pthread_attr_init(&attr);
pthread_attr_setdetachstat(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&th, &attr, thread, NULL);
...
}

调度属性

算法

设置调度算法的接口:​​pthread_attr_setschedpolicy(pthread_attr_t* attr, int policy);​​

policy:

  • ​​SCHED_RR​​:
  • ​​SCHED_FIFO​​:
  • ​​SCHED_OTHER​​:
轮询

POSIX 规定。实时调度算法。时间片轮转,当线程时间片用完时,系统重新分配时间片,并将它放置在就绪队列尾部,保证相同优先级的轮询任务获得公平的 CPU 占用时间。

先进先出

POSIX 规定。实时调度算法。一旦线程CPU则一直运行,直到有更高优先级的线程出现或者自己放弃。

其它

Linux 默认。

优先级

Linux 的线程和进程的优先级不一样,线程优先级是从 1~99 的数值,越大优先级越高,仅仅对 ​​SCHED_RR​​​、​​SCHED_FIFO​​​ 有用,​​SCHED_OTHER​​ 优先级恒为 0。

设置方式:

struct sched_param{
int sched_priority; // 线程的优先级
}
int pthread_attr_setschedparam(pthread_attr_t* attr, struct sched_param* param);

进程必须要以root方式运行,此外,还要放弃线程的继承权。

继承权

继承权就是当创建新的线程时,新线程要继承父线程(创建线程)的调度属性,如果不希望新线程继承父线程的调度属性,就要放弃继承权。新线程默认情况下拥有继承权。

​​int pthread_attr_setinheritsched(pthread_attr_t* attr, int inheritsched);​​

  • ​​PTHREAD_INHERIT_SCHED​​: 拥有继承权;
  • ​​PTHREAD_EXPLICIT_SCHED​​: 放弃继承权;
// 没有输出??
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#define THREAD_COUNT 12

void show_thread_policy( int threadno )
{
int policy;
struct sched_param param;
pthread_getschedparam( pthread_self(), &policy, &param );
switch( policy ){
case SCHED_OTHER:
printf( "SCHED_OTHER %d\n", threadno );
break;
case SCHED_RR:
printf( "SCHDE_RR %d\n", threadno );
break;
case SCHED_FIFO:
printf( "SCHED_FIFO %d\n", threadno );
break;
default:
printf( "UNKNOWN\n");
}
}
void* thread( void *arg )
{
int i, j;
long threadno = (long)arg;
printf( "thread %d start\n", threadno );
sleep(1);
show_thread_policy( threadno );
for( i = 0; i < 10; ++i ) {
for( j = 0; j < 100000000; ++j ){}
printf( "thread %d\n", threadno );
}
printf( "thread %d exit\n", threadno );
return NULL;
}
int main( int argc, char *argv[] )
{
long i;
pthread_attr_t attr[THREAD_COUNT];
pthread_t pth[THREAD_COUNT];
struct sched_param param;
for( i = 0; i < THREAD_COUNT; ++i ) // 一共运行12次,每次创建12个线程
pthread_attr_init( &attr[i] ); // 初始化线程属性对象
for( i = 0; i < THREAD_COUNT / 2; ++i ) {
param.sched_priority = 10; // 设定优先级值
pthread_attr_setschedpolicy( &attr[i], SCHED_FIFO ); // 调度方式设置
pthread_attr_setschedparam( &attr[i], &param ); // 优先级
pthread_attr_setinheritsched( &attr[i], PTHREAD_EXPLICIT_SCHED ); // 继承权 放弃继承权
}
for( i = THREAD_COUNT / 2; i < THREAD_COUNT; ++i ) {
param.sched_priority = 20;
pthread_attr_setschedpolicy( &attr[i], SCHED_FIFO );
pthread_attr_setschedparam( &attr[i], &param );
pthread_attr_setinheritsched( &attr[i], PTHREAD_EXPLICIT_SCHED );
}
for( i = 0; i < THREAD_COUNT; ++i )
pthread_create( &pth[i], &attr[i], thread, (void*)i ); // 循环创建12个线程
for( i = 0; i < THREAD_COUNT; ++i )
pthread_join( pth[i], NULL ); // 等待所有线程结束
for( i = 0; i < THREAD_COUNT; ++i )
pthread_attr_destroy( &attr[i] ); // 销毁线程属性对象
return 0;
}

堆栈大小属性

线程的入口函数同样需要保存局部变量,线程间的局部变量不共享,因为不同的线程拥有不同的堆栈,Linux 为每个线程默认分配了 8MB 堆栈空间,可以修改此值。

修改堆栈空间接口:​​int pthread_attr_setstacksize(pthread_attr_t* attr, size_t stacksize);​​,线程堆栈不能小于 16KB,尽量按照 4KB(32-bit)、2MB(64-biT) 的整数倍分配,

满占警戒区大小属性

Linux 为线程堆栈设置了一个满栈警戒区。这个区域一般就是一个页面,属于线程堆栈的一个扩展区域。一旦有代码访问了这个区域,就会发出 ​​SIGSEGV​​ 信号进行通知。

通过白白浪费空间的方式来保证安全,可以关闭这个警戒区。

​​int pthread_attr_setguardsize(pthread_attr_t* attr, size_t guardsize);​​ guard_size 以字节为单位来设置警戒区大小,为 0 时关闭禁戒区。当修改了线程堆栈的大小,就一定要同时修改警戒区。


线程之间可以共享地址空间,线程之间的数据交换可以非常快捷,这是线程最显著的优点。在很多时候使用多线程的目的并不是为了对共享数据进行并行处理,更多的是充分利用 CPU 资源而进行并行计算,大多数情况下每个线程只会关心自己的数据而不需要与别人同步。

线程的同步:

  • 互斥锁;
  • 信号量;


上一篇:【Zynq】Zynq UltraScale MPSoCs
下一篇:没有了
网友评论