Linux-c 线程总结

xiaoxiao2021-02-28  76

上周看了看了进程,这周看了线程,我觉得最能区别两者不同的就是楚东方学长讲firefox是线程管理,chrome是进程管理的,线程是共用地址空间,所以会比进程切换速度更快

一、线程的创建

#include<pthread.h> int pthread_create(pthread_t *thread,pthrad_attr_t *attr,void*(*start_routine)(void *),void*arg) 参数含义thread指针,当线程成功创建的时候,返回创建线程IDattr用于指定线程的属性,NULL为默认属性start_routine函数指针,指向线程创建后要调用的函数arg该参数指向传递给线程函数的参数

线程创建成功,函数返回0,记得gcc的时候加-lpthread

函数含义pthread_t pthread_self(void)获取本线程的线程IDint pthread_equal(pthread_t pthread1,pthread_t pthread2)判断两个线程ID是否指向同一个线程,是同一个返回1int pthread_once(pthread_once_t *once_control,void(*init_routine)(void)保证init_routine线程函数在进程中仅执行一次

arr属性相关函数

//初始化一个pthread_attr类型 int pthread_attr_init(pthread_attr_t *attr); //销毁一个pthread_attr类型 int pthread_attr_destroy(pthread_attr_t *attr); //设置/获取一个线程的分离状态,其中detachstate可以设置为: //①PTHREAD_CREATE_DETACHED 分离状态 //②PTHREAD_CREATE_JOINABLE 正常状态 int pthread_attr_setdetachstate(pthread_attr_t *attr,int detachstate); int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate); //设置栈的开始地址 int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize); int pthread_attr_getstack(const pthread_attr_t *attr, void **stackaddr, size_t *stacksize); //设置/获取线程的栈大小 int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize); int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize); //设置/获取栈警戒缓冲区的大小 int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize); int pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize); #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<pthread.h> #include<stdlib.h> int *thread(void *arg) { pthread_t newthid; newthid=pthread_self(); printf("this is a new thread,thread ID=%u\n",newthid); return NULL; } int main() { pthread_t thid; printf("main thread,ID is %u\n",pthread_self()); if(pthread_create(&thid,NULL,(void*)thread,NULL)!=0) { printf("thread creation failed\n"); exit(1); } sleep(1); exit(0); } pthread_once函数的使用 #include<stdio.h> #include<unistd.h> #include<pthread.h> pthread_once_t once=PTHREAD_ONCE_INIT; void run(void) { printf("function run is running in thread %u\n",pthread_self()); } void *thread1(void *arg) { pthread_t thid=pthread_self(); printf("current thread's ID is %u\n",thid); pthread_once(&once,run); printf("thread1 ends\n"); } void *thread2(void *arg) { pthread_t thid=pthread_self(); printf("current thread's ID is %u\n",thid); pthread_once(&once,run); printf("thread2 ends\n"); } int main() { pthread_t thid1,thid2; pthread_create(&thid1,NULL,thread1,NULL); //run函数只会在线程1运行一次 pthread_create(&thid2,NULL,thread2,NULL); sleep(3); printf("main thread exit! \n"); }

二、线程的终止

线程终止有两种方法

通过return从线程函数返回通过调用pthread_exit( )使线程退出 注意 从main函数里调用exit函数,主进程结束,所有线程结束 调用pthread_exit只会结束单一线程 #include<pthread.h> void pthread_exit(void *retval);

临界资源被一个线程所使用,当一个线程结束的时候,如果不释放其占有的临界资源,则该资源会被认为还被已经退出的线程所使用,因而永远也不会得到释放,如果另外一个线程等待使用这个临界资源,它就会无限等待,形成死锁 于是linux提供一对函数

void pthread_cleanup_push(void (*routine) (void *), void *arg) void pthread_cleanup_pop(int execute) pthread_cleanup_push()/pthread_cleanup_pop()采用先入后出的栈结构管理,void routine(void *arg)函数在调用pthread_cleanup_push()时压入清理函数栈,多次对pthread_cleanup_push()的调用将在清理函数栈中形成一个函数链,在执行该函数链时按照压栈的相反顺序弹出。execute参数表示执行到pthread_cleanup_pop()时是否在弹出清理函数的同时执行该函数,为0表示不执行,非0为执行;这个参数并不影响异常终止时清理函数的执行。 pthread_cleanup_push()/pthread_cleanup_pop()是以宏方式实现的,这是pthread.h中的宏定义: #define pthread_cleanup_push(routine,arg) { struct _pthread_cleanup_buffer _buffer; _pthread_cleanup_push (&_buffer, (routine), (arg)); #define pthread_cleanup_pop(execute) _pthread_cleanup_pop (&_buffer, (execute)); } 可见,pthread_cleanup_push()带有一个"{",而pthread_cleanup_pop()带有一个"}",因此这两个函数必须成对出现,且必须位于程序的同一级别的代码段中才能通过编译。

三、线程的等待

int pthread_join(pthread_t thread, void **retval);

返回值:若函数执行成功,则返回0,发生错误则会返回错误码。 thread:要获取终止状态的线程ID。 retval:线程的终止状态将写入retval指向的内存区域。

#include<stdio.h> #include<pthread.h> void assisthread(void *arg) { printf("i am helping to do some jobs\n"); sleep(5); pthread_exit(0); } int main() { pthread_t assistthid; int status; pthread_create(&assistthid,NULL,(void *)assisthread,NULL); pthread_join(assistthid,(void*)&status); printf("assisthread's exit is caused %d\n",status); return 0; }

四、私有数据(一键多值) 比如errno是一个全局变量,每个线程可能出错的原因都不一样,所以这时候不能讲errno看做是一个全局变量。这时候就必须用到一键多值

#include<pthread.h> int pthread_key_creat(pthread_key_t*key,void(*destr_function)(void *)); int pthread_setspecific(pthread_key_t key,const void *pointer); void *pthread_getspecific(pthread_key_t key); int pthread_key_delete(pthread_key_t key); #include<stdio.h> #include<unistd.h> #include<string.h> #include<pthread.h> pthread_key_t key; int tsd; void *thread2(void *arg) { tsd=5; printf("thread2 %d is running\n",pthread_self()); pthread_setspecific(key,(void *)tsd); printf("thread2 %d returns %d\n",pthread_self(),pthread_getspecific(key)); } void *thread1(void *arg) { tsd=0; pthread_t thid2; printf("thread1 %d is running\n",pthread_self()); pthread_setspecific(key,(void *)tsd); pthread_create(&thid2,NULL,thread2,NULL); sleep(5); printf("thread1 %d returns %d\n",pthread_self(),pthread_getspecific(key)); } int main() { pthread_t thid1; printf("main thread begins running\n"); pthread_key_create(&key,NULL); pthread_create(&thid1,NULL,thread1,NULL); sleep(8);//时间尽量长一点,让thread2和thread1都结束才可以 pthread_key_delete(key); printf("main thread exit\n"); return 0; }

四、线程同步

1、互斥锁

int pthread_mutex_init(pthread_mutex_t *restrict mutex, const thread_mutexattr_t *restrict attr);//初始化 //对于动态分配的互斥量,在释放他的空间之前必须调用销毁函数 int pthread_mutex_destroy(pthread_mutex_t *mutex); //尝试加锁,若互斥量不能加锁,则阻塞调用线程。 int pthread_mutex_lock(pthread_mutex_t *mutex); //尝试加锁,若互斥量不能加锁,则直接返回错误码。若可以加锁,将锁住互斥量。 int pthread_mutex_trylock(pthread_mutex_t *mutex); //解锁 int pthread_mutex_unlock(pthread_mutex_t *mutex);

2、条件变量 条件变量是利用线程间共享的全局变量进行同步的一种机制。类似if语句,符合条件就能执行某段代码,否则只能等待

//类似于互斥量、读写锁,静态分配的条件变量可以使用以下两种初始化方式: pthread_cond_t cond = PTHREAD_COND_INITIALIZER; //动态分配的条件变量必须使用以下这种初始化方式: int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr); //无论静态分配还是动态分配,都应当在不再使用条件变量时销毁它 int pthread_cond_destroy(pthread_cond_t *cond); 返回值:若函数执行成功则返回0,失败则返回表示出错类型的错误码。 cond:要初始化、销毁的条件变量。 attr:新的条件变量的属性,若我们想要以默认的形式创建一个条件变量,则可以将这个属性设置为NULL //等待条件为真,在此期间会解锁保护条件变量的互斥量 //但当wait返回时会再次锁住保护它的互斥量 int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime); 返回值:若函数执行成功则返回0,反之则返回表示错误类型的错误码。 cond:要等待的条件变量。 mutex:保护条件变量的互斥量。 abstime:在等待条件变量为真之前最长可以等待的时间,这里同互斥量一样,是一个绝对时间。 //至少可以唤醒一个等待该条件的线程 int pthread_cond_signal(pthread_cond_t *cond); //可以唤醒全部等待该条件的线程 int pthread_cond_broadcast(pthread_cond_t *cond); 返回值:若函数执行成功返回0,反之则返回错误码。 cond:要唤醒的条件。 #include<stdio.h> #include<unistd.h> #include<pthread.h> pthread_mutex_t mutex; pthread_cond_t cond; void *thread1(void *arg) { pthread_cleanup_push(pthread_mutex_unlock,&mutex); while(1) { printf("thread1 is running\n"); pthread_mutex_lock(&mutex); pthread_cond_wait(&cond,&mutex); printf("thread1 applied the condition\n"); pthread_mutex_unlock(&mutex); sleep(4); } pthread_cleanup_pop(0); } void *thread2(void *arg) { while(1) { printf("thread2 is running\n"); pthread_mutex_lock(&mutex); pthread_cond_wait(&cond,&mutex); printf("thread2 applied the condition\n"); pthread_mutex_unlock(&mutex); sleep(5); } } int main() { pthread_t tid1,tid2; printf("condition variable study!\n"); pthread_mutex_init(&mutex,NULL); pthread_cond_init(&cond,NULL); pthread_create(&tid1,NULL,(void *)thread1,NULL); pthread_create(&tid2,NULL,(void *)thread2,NULL); do{ pthread_cond_signal(&cond); }while(1); sleep(50); pthread_exit(0); }
转载请注明原文地址: https://www.6miu.com/read-43433.html

最新回复(0)