线程属性
int pthread_create (pthread_t* restrict thread,const pthread_attr_t* restrict attr,void* (*start_routine) (void*),void* restrict arg); //创建线程函数的第二个参数即为线程属性,传空指针表示使用缺省属性。 typedef struct {// 分离状态int detachstate;// PTHREAD_CREATE_DETACHED - 分离线程。// PTHREAD_CREATE_JOINABLE(缺省) - 可汇合线程。 // 竞争范围int scope;// PTHREAD_SCOPE_SYSTEM - 在系统范围内竞争资源(时间片)。// PTHREAD_SCOPE_PROCESS(Linux不支持) - 在进程范围内竞争资源。// 继承特性int inheritsched;// PTHREAD_INHERIT_SCHED(缺省) - 调度属性自创建者线程继承。// PTHREAD_EXPLICIT_SCHED - 调度属性由后面两个成员确定。// 调度策略nt schedpolicy;// SCHED_FIFO - 先进先出策略。// 没有时间片。// 一个FIFO线程会持续运行,直到阻塞或有高优先级线程就绪。// 当FIFO线程阻塞时,系统将其移出就绪队列,待其恢复时再加到同优先级就绪队列的末尾。// 当FIFO线程被高优先级线程抢占时,它在就绪队列中的位置不变。// 因此一旦高优先级线程终止或阻塞,被抢占的FIFO线程将会立即继续运行。// SCHED_RR - 轮转策略。// 给每个RR线程分配一个时间片,一但RR线程的时间片耗尽,系统即将移到就绪队列的末尾。// SCHED_OTHER(缺省) - 普通策略。// 静态优先级为0。任何就绪的FIFO线程或RR线程,都会抢占此类线程。 // 调度参数struct sched_param schedparam;// struct sched_param {// int sched_priority; /* 静态优先级 */// };// 栈尾警戒区大小(字节) 缺省一页(4096字节)。size_t guardsize; // 栈地址void* stackaddr; // 栈大小(字节)size_t stacksize; } pthread_attr_t; 注意:不要手动读写该结构体,而应调用pthread_attr_set/get函数设置/获取具体属性项。
设置线程属性:
初始化线程属性结构体:
pthread_attr_t attr = {}; // 不要使用这种方式 int pthread_attr_init (pthread_attr_t* attr);
设置具体线程属性项:
int pthread_attr_setdetachstate (pthread_attr_t* attr,int detachstate); int pthread_attr_setscope (pthread_attr_t* attr,int scope); int pthread_attr_setinheritsched (pthread_attr_t* attr,int inheritsched); int pthread_attr_setschedpolicy (pthread_attr_t* attr,int policy); int pthread_attr_setschedparam (pthread_attr_t* attr,const struct sched_param* param); int pthread_attr_setguardsize (pthread_attr_t* attr,size_t guardsize); int pthread_attr_setstackaddr (pthread_attr_t* attr,void* stackaddr); int pthread_attr_setstacksize (pthread_attr_t* attr,size_t stacksize); int pthread_attr_setstack (pthread_attr_t* attr,void* stackaddr, size_t stacksize);
以设置好的线程属性结构体为参数创建线程:
int pthread_create (pthread_t* restrict thread,const pthread_attr_t* testrict attr,void* (*start_routine) (void*),void* restrict arg);
销毁线程属性结构体:
int pthread_attr_destroy (pthread_attr_t* attr);
获取线程属性:
获取线程属性结构体:
int pthread_getattr_np (pthread_t thread,pthread_attr_t* attr);
获取具体线程属性项:
int pthread_attr_getdetachstate (pthread_attr_t* attr,int* detachstate); int pthread_attr_getscope (pthread_attr_t* attr,int* scope); int pthread_attr_getinheritsched (pthread_attr_t* attr,int* inheritsched); int pthread_attr_getschedpolicy (pthread_attr_t* attr,int* policy); int pthread_attr_getschedparam (pthread_attr_t* attr,struct sched_param* param); int pthread_attr_getguardsize (pthread_attr_t* attr,size_t* guardsize); int pthread_attr_getstackaddr (pthread_attr_t* attr,void** stackaddr); int pthread_attr_getstacksize (pthread_attr_t* attr,size_t* stacksize); int pthread_attr_getstack (pthread_attr_t* attr,void** stackaddr, size_t* stacksize); 以上所有函数成功返回0,失败返回错误码。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #define __USE_GNU #include <pthread.h> int printattrs (pthread_attr_t* attr) {printf("------- 线程属性 -------\n"); int detachstate;int error = pthread_attr_getdetachstate (attr, &detachstate);if (error) {fprintf (stderr, "pthread_attr_getdetachstate: %s\n",strerror (error));return -1;}printf("分离状态: %s\n",(detachstate == PTHREAD_CREATE_DETACHED) ? "分离线程" :(detachstate == PTHREAD_CREATE_JOINABLE) ? "可汇合线程" :"未知"); int scope;if ((error = pthread_attr_getscope (attr, &scope)) != 0) {fprintf (stderr, "pthread_attr_getscope: %s\n",strerror (error));return -1;}printf ("竞争范围: %s\n",(scope == PTHREAD_SCOPE_SYSTEM) ? "系统级竞争" :(scope == PTHREAD_SCOPE_PROCESS) ? "进程级竞争" : "未知"); int inheritsched;if ((error = pthread_attr_getinheritsched (attr,&inheritsched)) != 0) {fprintf (stderr, "pthread_attr_getinheritsched: %s\n",strerror (error));return -1;}printf ("继承特性: %s\n",(inheritsched == PTHREAD_INHERIT_SCHED) ? "继承调用属性" :(inheritsched == PTHREAD_EXPLICIT_SCHED) ? "显式调用属性" :"未知"); int schedpolicy;if ((error = pthread_attr_getschedpolicy(attr,&schedpolicy)) != 0) {fprintf (stderr, "pthread_attr_getschedpolicy: %s\n",strerror (error));return -1;}printf ("调度策略: %s\n",(schedpolicy == SCHED_OTHER) ? "普通" :(schedpolicy == SCHED_FIFO) ? "先进先出" :(schedpolicy == SCHED_RR) ? "轮转" : "未知"); struct sched_param schedparam;if ((error = pthread_attr_getschedparam (attr, &schedparam)) != 0) {fprintf (stderr, "pthread_attr_getschedparam: %s\n",strerror (error));return -1;}printf ("调度优先级:%d\n", schedparam.sched_priority); size_t guardsize;if ((error = pthread_attr_getguardsize(attr, &guardsize)) != 0) {fprintf (stderr, "pthread_attr_getguardsize: %s\n",strerror (error));return -1;}printf ("栈尾警戒区:%u字节\n", guardsize);/*void* stackaddr;if ((error = pthread_attr_getstackaddr (attr, &stackaddr)) != 0) {fprintf (stderr, "pthread_attr_getstackaddr: %s\n",strerror (error));return -1;}printf ("栈地址: %p\n", stackaddr); size_t stacksize;if ((error = pthread_attr_getstacksize (attr, &stacksize)) != 0) {fprintf (stderr, "pthread_attr_getstacksize: %s\n",strerror (error));return -1;}printf ("栈大小: %u字节\n", stacksize);*/void* stackaddr;size_t stacksize;if ((error = pthread_attr_getstack (attr, &stackaddr,&stacksize)) != 0) {fprintf (stderr, "pthread_attr_getstack: %s\n",strerror (error));return -1;}printf ("栈地址: %p\n", stackaddr);printf ("栈大小: %u字节\n", stacksize); printf("------------------------\n"); return 0; } void* thread_proc (void* arg) {pthread_attr_t attr;int error = pthread_getattr_np (pthread_self (), &attr);if (error) {fprintf (stderr, "pthread_getattr_np: %s\n", strerror (error));exit (EXIT_FAILURE);} if (printattrs (&attr) < 0)exit (EXIT_FAILURE); exit (EXIT_SUCCESS); return NULL; } int main (int argc, char* argv[]) {int error;pthread_attr_t attr, *pattr = NULL; if (argc > 1) {if (strcmp (argv[1], "-s")) {fprintf (stderr, "用法:%s [-s]\n", argv[0]);return -1;} if ((error = pthread_attr_init (&attr)) != 0) {fprintf (stderr, "pthread_attr_init: %s\n",strerror (error));return -1;} if ((error = pthread_attr_setdetachstate (&attr,PTHREAD_CREATE_DETACHED)) != 0) {fprintf (stderr, "pthread_attr_setdetachstate: %s\n",strerror (error));return -1;} if ((error = pthread_attr_setinheritsched (&attr,PTHREAD_EXPLICIT_SCHED)) != 0) {fprintf (stderr, "pthread_attr_setinheritsched: %s\n",strerror (error));return -1;} if ((error = pthread_attr_setstacksize (&attr, 4096*10)) != 0) {fprintf (stderr, "pthread_attr_setstack: %s\n",strerror (error));return -1;} pattr = &attr;} pthread_t tid;if ((error = pthread_create (&tid, pattr, thread_proc,NULL)) != 0) {fprintf (stderr, "pthread_create: %s\n", strerror (error));return -1;} if (pattr){if ((error = pthread_attr_destroy (pattr)) != 0) {fprintf (stderr, "pthread_attr_destroy: %s\n",strerror (error));return -1;}} pause ();return 0; }
注意:如果man手册查不到线程的相关函数,安装完整版gnu手册:sudo apt-get install glibc-doc。
练习:实现大文件的多线程cp拷贝,对比系统cp命令,哪个速度更快,为什么?
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <sys/stat.h> #include <sys/types.h> typedef struct Task {char* src;char* dest;size_t start;size_t end; }Task; void* run(void* arg) {Task* task = arg; // 打开源文件和目标文件FILE* src_fp = fopen(task->src,"r");FILE* dest_fp = fopen(task->dest,"a");if(NULL == src_fp || NULL == dest_fp){perror("fopen");return NULL;} // 调整文件的位置指针fseek(src_fp,task->start,SEEK_SET);fseek(dest_fp,task->start,SEEK_SET); // 创建缓冲区char buf[1024];size_t buf_size = sizeof(buf); for(int i=task->start; i<task->end; i+=buf_size){int ret = fread(buf,1,buf_size,src_fp);if(0 >= ret)break;fwrite(buf,1,ret,dest_fp);} fclose(src_fp);fclose(dest_fp);free(task); } int main(int argc,const char* argv[]) {if(3 != argc){puts("Use:./cp <src> <dest>");return 0;} // 获取到文件的大小struct stat buf;if(stat(argv[1],&buf)){perror("stat");return -1;} // 创建出目标文件if(NULL == fopen(argv[2],"w")){perror("fopen");return -2;} // 计算需要的线程数量,以100M为单位size_t pthread_cnt = buf.st_size/(1024*1024*100)+1; // 分配任务pthread_t tid;for(int i=0; i<pthread_cnt; i++){Task* task = malloc(sizeof(Task));task->src = (char*)argv[1];task->dest = (char*)argv[2];task->start = i*1024*1024*100;task->end = (i+1)*1024*1024*100; // 创建子线程并分配任务pthread_create(&tid,NULL,run,task); // 分享子线程pthread_detach(tid);}// 结束主线程pthread_exit(NULL); }
多线程并不能提高运行速度,反而可能会降低,所以多线程不适合解决运算密集性问题,而是适合解决等待、阻塞的问题,如果使用进程去等待,会浪费大量资源,所以使用更轻量的线程去等待,节约资源。