自己建网站怎么做影视资源,牛搬家网企业网站排名,网页设计背景图,商标注册费用一般是多少钱线程总结1 线程的实现线程创建线程退出线程等待线程清理2 线程的属性线程的分离线程的栈地址线程栈大小线程的调度策略线程优先级3 线程的同步互斥锁读写锁条件变量信号量线程是系统独立调度和分配的基本单位。同一进程中的多个线程将共享该进程中的全部系统资源#xff0c;例…
线程总结1 线程的实现线程创建线程退出线程等待线程清理2 线程的属性线程的分离线程的栈地址线程栈大小线程的调度策略线程优先级3 线程的同步互斥锁读写锁条件变量信号量线程是系统独立调度和分配的基本单位。同一进程中的多个线程将共享该进程中的全部系统资源例如文件描述符和信号处理等。一个进程可以有很多线程每个线程并发执行不同的任务。
1 线程的实现
线程创建
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);thread指向线程标识符的指针当线程创建成功后用来返回创建的线程IDatt指定线程的属性NULL表示默认start_routine函数指针指向线程创建后要调用的函数直接赋值函数名即可arg是传给函数的参数
#include stdio.h
#include stdlib.h
#include unistd.h
#include pthread.hvoid * my_pthread(void *arg)
{printf(pthread id %ld\n,pthread_self());return NULL;
}int main(void)
{pthread_t pid;if(pthread_create(pid,NULL,my_pthread,NULL)){printf(pthread_create error\n);exit(1);}sleep(2);return 0;
}线程退出
void pthread_exit(void *retval);retval是线程结束时的返回值可由其他函数如pthread_join来获取
注意如果进程中任何一个线程调用exit()或_exit()函数那么整个进程就会终止。线程的正常退出方法有线程从线程函数中返回return、线程可以被另一个线程终止以及线程自己调用pthread_exit()
线程等待
在调用pthread_create函数后就会运行相关的线程函数。pthread_join()是一个线程阻塞函数调用后调用者一直等待指定的线程结束才返回被等待线程的资源就会被回收。
int pthread_join(pthread_t thread, void **retval);pthread是等待结束的线程idretval是用户定义的指针用来存储被等待线程结束时的返回值。
#include stdio.h
#include stdlib.h
#include unistd.h
#include pthread.hvoid * my_pthread(void *arg)
{int ret 5;printf(pthread id %ld\n,pthread_self());pthread_exit((void *)ret);
}int main(void)
{pthread_t pid;void *ret ;if(pthread_create(pid,NULL,my_pthread,NULL)){printf(pthread_create error\n);exit(1);}pthread_join(pid,ret);printf(ret %d\n,*(int * )ret);return 0;
}线程函数运行结束是可以有返回值的这个函数的返回值怎么返回呢可以通过return语句进行返回也可以通过pthread_exit()函数进行返回。函数的这个返回值怎么来接收呢就通过pthread_join()函数来接受。 当然也可以选择不接受该线程的返回值只阻塞该线程
pthread_join(tid, NULL);线程清理
线程终止有两种情况正常终止和非正常终止。线程主动调用pthread_exit()或者从线程中return都使进程正常退出。非正常终止时线程在其他线程的干预下停止运行或者由于自身运行错误而退出。
线程可以安排它退出时需要调用的函数这与进程在退出时可以用atexit函数安排退出函数是类似的。这样的函数称为线程清理处理程序。一个线程可以建立多个清理处理程序。
void pthread_cleanup_push(void (* rtn)(void *), void * arg);函数说明将清除函数压入清除栈。rtn是清除函数arg是清除函数的参数。
void pthread_cleanup_pop(int execute);函数说明将清除函数弹出清除栈。执行到pthread_cleanup_pop()时参数execute决定是否在弹出清除函数的同时执行该函数execute非0时执行execute为0时不执行。
从pthread_cleanup_push的调用点到pthread_cleanip_pop之间的程序段中的终止动作包括调用pthread_exit()和异常终止不包括return都将执行pthread_cleanup_push()所指定的清理函数。
int pthread_cancel(pthread_t thread);函数说明取消线程该函数在其他线程中调用用来强行杀死指定的线程。
2 线程的属性
参考文章
https://blog.csdn.net/zsf8701/article/details/7842392https://blog.csdn.net/qq_22847457/article/details/89461222https://blog.csdn.net/yychuyu/article/details/84503261?spm1001.2101.3001.6650.1utm_mediumdistribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1.pc_relevant_antiscanv2depth_1-utm_sourcedistribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1.pc_relevant_antiscanv2utm_relevant_index2
线程属性标识符pthread_attr_t 包含在 pthread.h 头文件中。
typedef struct
{int detachstate; //线程的分离状态int schedpolicy; //线程调度策略structsched_param schedparam; //线程的调度优先级int inheritsched; //线程的继承性int scope; //线程的作用域size_t guardsize; //线程栈末尾的警戒缓冲区大小int stackaddr_set; //线程的栈设置void* stackaddr; //线程栈的位置size_t stacksize; //线程栈的大小
}pthread_attr_t;属性值不能直接设置须使用相关函数进行操作初始化的函数为pthread_attr_init这个函数必须在pthread_create函数之前调用。之后须用pthread_attr_destroy函数来释放资源。线程属性主要包括如下属性作用域scope、栈尺寸stack size、栈地址stack address、优先级priority、分离的状态detached state、调度策略和参数scheduling policy and parameters。默认的属性为非绑定、非分离、缺省1M的堆栈、与父进程同样级别的优先级。
线程的分离
分离状态属性确定使用线程属性对象 attr 创建的线程是在可连接状态还是可分离状态下创建。
如果在创建线程时就知道不需要了解线程的终止状态就可以修改pthread_attr_t结构中的detachstate线程属性让线程一开始就处于分离状态。可以使用pthread_attr_setdetachstate函数把线程属性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);
注意事项如果设置一个线程为分离线程而这个线程运行又非常快它很可能在pthread_create函数返回之前就终止了
它终止以后就可能将线程号和系统资源移交给其他的线程使用这样调用pthread_create的线程就得到了错误的线程号。
解决方法要避免这种情况可以采取一定的同步措施最简单的方法之一是可以在被创建的线程里调用pthread_cond_timedwait函数让这个线程等待一会儿留出足够的时间让函数pthread_create返回。设置一段等待时间是在多线程编程里常用的方法。
线程的栈地址
POSIX.1定义了两个常量_POSIX_THREAD_ATTR_STACKADDR 和_POSIX_THREAD_ATTR_STACKSIZE检测系统是否支持栈属性。也可以给sysconf函数传递_SC_THREAD_ATTR_STACKADDR或 _SC_THREAD_ATTR_STACKSIZE来进行检测。
当进程栈地址空间不够用时指定新建线程使用由malloc分配的空间作为自己的栈空间。通过pthread_attr_setstack和pthread_attr_getstack两个函数分别设置和获取线程的栈地址。
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);
线程栈大小
当系统中有很多线程时可能需要减小每个线程栈的默认大小防止进程的地址空间不够用。当线程调用的函数会分配很大的局部变量或者函数调用层次很深时可能需要增大线程栈的默认大小。
函数pthread_attr_getstacksize和 pthread_attr_setstacksize可以设置或者获取线程的栈大小。 int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize);
线程的调度策略
POSIX标准指定了三种调度策略先入先出策略 (SCHED_FIFO)、循环策略 (SCHED_RR) 和自定义策略 (SCHED_OTHER)。SCHED_FIFO 是基于队列的调度程序对于每个优先级都会使用不同的队列。SCHED_RR 与 FIFO 相似不同的是前者的每个线程都有一个执行时间配额。SCHED_FIFO 和 SCHED_RR 是对 POSIX Realtime 的扩展。SCHED_OTHER 是缺省的调度策略。
新线程默认使用 SCHED_OTHER 调度策略。线程一旦开始运行直到被抢占或者直到线程阻塞或停止为止。SCHED_FIFO 如果调用进程具有有效的用户 ID 则争用范围为系统 (PTHREAD_SCOPE_SYSTEM) 的先入先出线程属于实时 (RT) 调度类。如果这些线程未被优先级更高的线程抢占则会继续处理该线程直到该线程放弃或阻塞为止。对于具有进程争用范围 (PTHREAD_SCOPE_PROCESS)) 的线程或其调用进程没有有效用户 ID 的线程请使用 SCHED_FIFOSCHED_FIFO 基于 TS 调度类。SCHED_RR 如果调用进程具有有效的用户 ID 则争用范围为系统 (PTHREAD_SCOPE_SYSTEM)) 的循环线程属于实时 (RT) 调度类。如果这些线程未被优先级更高的线程抢占并且这些线程没有放弃或阻塞则在系统确定的时间段内将一直执行这些线程。对于具有进程争用范围 (PTHREAD_SCOPE_PROCESS) 的线程请使用 SCHED_RR(基于 TS 调度类)。此外这些线程的调用进程没有有效的用户 ID 。 int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy);参数policy可以为SCHED_FIFO, SCHED_RR, and SCHED_OTHER
线程优先级
线程优先级存放在结构sched_param中
struct sched_param {int sched_priority; /* Scheduling priority */};
可以通过pthread_attr_setschedparam()和pthread_attr_getschedparam设置和获取线程的调度优先级 它的完整定义是 int pthread_attr_setschedparam(pthread_attr_t *attr,const struct sched_param *param);int pthread_attr_getschedparam(const pthread_attr_t *attr,struct sched_param *param);
3 线程的同步
参考文章
https://blog.csdn.net/qq_43412060/article/details/106989170 https://www.cnblogs.com/wsw-seu/p/8036218.html
互斥锁
互斥锁用pthread_mutex_t数据类型表示互斥锁可以用来控制线程对共享资源的互斥访问确保同一时间只有一个线程访问数据 。
/* 初始化互斥锁 */
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
/* 销毁互斥锁 */
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);接下来我们来实现主线程负责接收用户输入函数线程负责将用户输入打印到终端界面
#includestdio.h
#includestdlib.h
#includeassert.h
#includestring.h
#includeunistd.h#includepthread.h
#includetime.h
#includefcntl.hpthread_mutex_t mutex;char buff[128] {0};void *fun(void *arg)
{while(1){pthread_mutex_lock(mutex);if(strncmp(buff,end,3) 0){break;}printf(fun :%s\n,buff);memset(buff,0,128);int n rand() % 3 1;sleep(n);pthread_mutex_unlock(mutex);n rand() % 3 1;sleep(n);}
}int main()
{srand((unsigned int)(time(NULL) * time(NULL)));pthread_mutex_init(mutex,NULL);//初始化的锁是解锁状态的//创建一个线程pthread_t id;int res pthread_create(id,NULL,fun,NULL);assert(res 0);while(1){pthread_mutex_lock(mutex);printf(input:);fgets(buff,127,stdin);pthread_mutex_unlock(mutex);if(strncmp(buff,end,3) 0){break;}int n rand()%3 1;sleep(n);}//等待函数线程的结束pthread_join(id,NULL);pthread_mutex_destroy(mutex);exit(0);
}读写锁
读写锁是更高级的互斥锁有更高的并行性。互斥锁只允许一个线程对临界区访问而读写锁可以让多个读者并发访问。 读写锁可以多个读者读但只允许一个写者写。
如果一个线程用读锁锁定了临界区那么其他线程也可以用读锁来进入临界区这样就可以多个线程并行操作。但这个时候如果再进行写锁加锁就会发生阻塞写锁请求阻塞后后面如果继续有读锁来请求这些后来的读锁都会被阻塞这样避免了读锁长期占用资源防止写锁饥饿如果一个线程用写锁锁住了临界区那么其他线程不管是读锁还是写锁都会发生阻塞
/* 读写锁的销毁 */
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
/* 读写锁的初始化 */
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);
pthread_rwlock_t rwlock PTHREAD_RWLOCK_INITIALIZER;/* 读写锁加锁解锁 */
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rwlock, const struct timespec *restrict abs_timeout);
int pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict rwlock, const struct timespec *restrict abs_timeout);int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);我们使用读写锁来实现两个线程读写一段数据 主线程写入时加写锁两个函数线程读取时加读锁这样两个函数线程可以同时读取
# includeunistd.h
#include stdlib.h
# includestdio.h
# includestring.h
# includetime.h
# includeassert.h
# includepthread.hpthread_rwlock_t rwlock;//创建读写锁
char buff[128]{0};void* fun(void* arg)//读数据
{while(1){pthread_rwlock_rdlock(rwlock);if(strncmp(buff,end,3)0){break;}printf(fun:%s\n,buff);//memset(buff,0,128);int nrand()%31;sleep(n);pthread_rwlock_unlock(rwlock);nrand()%31;sleep(1);}
}
void* fun1(void* arg)//读数据
{while(1){pthread_rwlock_rdlock(rwlock);if(strncmp(buff,end,3)0){break;}printf(fun1:%s\n,buff);//memset(buff,0,128);int nrand()%31;sleep(n);pthread_rwlock_unlock(rwlock);nrand()%31;sleep(1);}
}
int main()
{srand((unsigned int)(time(NULL)*time(NULL)));pthread_rwlock_init(rwlock,NULL);//初始化pthread_t id[2];int respthread_create(id[0],NULL,fun,NULL);//创建线程int rpthread_create(id[1],NULL,fun1,NULL);assert(res0);while(1)//写数据{pthread_rwlock_wrlock(rwlock);printf(input:);fgets(buff,127,stdin);pthread_rwlock_unlock(rwlock);if(strncmp(buff,end,3)0){break;}int nrand()%31;sleep(1);}pthread_join(id[0],NULL);pthread_join(id[1],NULL);pthread_rwlock_destroy(rwlock);
}条件变量
条件变量是利用线程间共享全局变量进行同步的一种机制。一个线程修改条件另一个线程等待条件一旦等到自己需要的条件就去运行。条件变量用pthread_cond_t类型的实例表示。
信号量
这个信号量和进程间用的信号量作用类似当线程访问一些有限的公共资源时就必须做到线程间同步访问。其实就类似于一个计数器有一个初始值用于记录临界资源的个数。信号量由sem_t的实例表示。