0
点赞
收藏
分享

微信扫一扫

pthread

玉字璧 2022-02-14 阅读 78
c语言

文章目录

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_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_RRSCHED_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 资源而进行并行计算,大多数情况下每个线程只会关心自己的数据而不需要与别人同步。

线程的同步:

  • 互斥锁;
  • 信号量;
举报

相关推荐

0 条评论