首页 / 资讯中心 / 文章详情

Linux 多线程编程入门:线程基础、创建、生产者消费者与线程安全

Linux 多线程编程入门:线程基础、创建、生产者消费者与线程安全 ★ FEATURED ARTICLE
基本知识线程进程内部的一条执行路径主线程结束则进程结束其他线程没运行完也不能运行了。两个线程如果实现功能相同则可以用同一个线程函数线程并发运行多个线程同时执行作用一个进程可能既需要接收键盘的数据又需要接收网络传递的数据对于多线程程序folk操作之后 父进程会执行两条执行路径fork函数在哪个线程中子进程就会执行哪条线程,即子进程只有一条执行路径线程分类操作系统角度分析线程实现分三种用户级线程并发同时发生一个处理器用户空间有多个线程在内核中只有一条处理路径内核级线程并行同时执行内核中有多条处理路径多个处理器混合模型用户级线程内核级线程并发与并行区别并发多个线程在一个处理器上交替执行多个任务交替执行看起来同时跑实际是轮流跑。并行多个处理器同时执行多个任务真正同时执行必须多核 CPU。普通创建线程编译需要链接线程库gcc -o test test.c -pthreadtest.c文件void *fun(void *arg)//线程函数 { for (int i 0; i 7; i) { printf(fun run\n); sleep(1); } pthread_exit(fun exit);//退出线程 } int main() { pthread_t id; //定义线程id pthread_create(id, NULL, fun, NULL);//创建线程 //第一个参数传id的地址创建成功后给id赋值最后一个参数是交给fun函数的参数 for (int i 0; i 2; i) { printf(main run\n); sleep(1); } char* s NULL; pthread_join(id, (void**)s);//等待线程结束 //最后一个参数传二级指针指向线程函数想返回给主函数的信息s让s指向fun exit printf(s%s\n,s); }代码执行主线程先循环2次2s子线程一共循环7次7s主线程循环结束后阻塞在 pthread_join 等子线程全部跑完才继续执行printf。生产者消费者问题生产者消费者问题也称为有限缓冲区问题图解多个生产者消费者可以并发就绪/运行但同一瞬间只有一个生产者在写缓冲区不会并发写因为互斥锁控制访问缓冲区的权限写要申请锁别人不能再申请两个生产者可以都先p但只有一个先设锁另一个生产者先等着同时3个消费者也等着。设锁的生产者操作完之后arr位置以已经往后挪了一位先p再加锁先加锁再p操作若为0则会阻塞导致死锁代码#include stdio.h #include stdlib.h #include unistd.h #include string.h #include pthread.h #include semaphore.h #define BUFF_MAX 30 #define SC_NUM 2 #define XF_NUM 3 int buff[BUFF_MAX] {0};//缓冲区 int in 0;//产生数据的位置 int out 0;//消费数据的位置 sem_t sem_empty;//代表空格子的数目生产者参考 sem_t sem_full;//代表有数据的格子数目消费者参考 pthread_mutex_t mutex;//控制对缓冲区的互斥性访问互斥锁 void* sc_fun(void* arg) { for(int i 0; i 30; i) { sem_wait(sem_empty);//p(s1) pthread_mutex_lock(mutex);//加锁 buff[in] rand() % 100;//产生数据 printf(生产者在%d,产生数据%d\n,in,buff[in]); in (in 1) % BUFF_MAX; pthread_mutex_unlock(mutex);//解锁 sem_post(sem_full);//v(s2) sleep(1); } } void* xf_fun(void* arg) { for(int i 0; i 20; i) { sem_wait(sem_full);//p(s2) pthread_mutex_lock(mutex);//加锁 printf(-----消费者在%d,位置打印 %d\n,out,buff[out]); out (out 1) % BUFF_MAX; pthread_mutex_unlock(mutex);//解锁 sem_post(sem_empty);//v(s1) sleep(1); } } int main() { pthread_mutex_init(mutex,NULL); sem_init(sem_empty,0,BUFF_MAX);//s1 sem_init(sem_full,0,0);//s2 pthread_t sc_id[SC_NUM];//生产者线程组 pthread_t xf_id[XF_NUM];//消费者线程组 for(int i 0; i SC_NUM; i) { pthread_create(sc_id[i],NULL,sc_fun,NULL);//创建两个生产者 } for(int i 0; i XF_NUM; i) { pthread_create(xf_id[i],NULL,xf_fun,NULL);//创建三个消费者 } //等待线程结束 for(int i 0; i SC_NUM;i) { pthread_join(sc_id[i],NULL); } for(int i 0; i XF_NUM;i) { pthread_join(xf_id[i],NULL); } sem_destroy(sem_empty); sem_destroy(sem_full); pthread_mutex_destroy(mutex); exit(0); }线程安全线程安全即程序运行正确性指对于多线程程序无论调度顺序如何都能得到正确的结果保证线程安全同步信号量互斥锁读写锁条件变量使用线程安全的函数也称为可重入函数举例通过条件变量实现线程安全#include stdio.h #include stdlib.h #include string.h #include unistd.h #include pthread.h pthread_mutex_t mutex;//互斥锁 pthread_cond_t cond;//条件变量 char buff[128] {0};//存放键盘输入的数据 void *funa(void* arg)//线程函数a { while(1) { pthread_mutex_lock(mutex);//加锁 pthread_cond_wait(cond, mutex); //wait放在等待队列中先进行解锁操作解锁前阻塞唤醒返回时进行加锁操作 pthread_mutex_unlock(mutex);//解锁 if( strncmp(buff, end, 3) 0 ) { break; } printf(funa buff%s\n,buff); } } void *funb(void* arg)//线程函数b { while(1) { pthread_mutex_lock(mutex); pthread_cond_wait(cond, mutex); pthread_mutex_unlock(mutex); if( strncmp(buff, end, 3) 0 ) { break; } printf(funb buff%s\n,buff); } } int main() { pthread_mutex_init(mutex, NULL); pthread_cond_init(cond, NULL); pthread_t ida, idb; pthread_create(ida, NULL, funa, NULL); pthread_create(idb, NULL, funb, NULL); while(1) { fgets(buff, 128, stdin); if( strncmp(buff, end, 3) 0 ) { pthread_mutex_lock(mutex); pthread_cond_broadcast(cond);//唤醒所有线程函数 pthread_mutex_unlock(mutex); break; } else { pthread_mutex_lock(mutex); pthread_cond_signal(cond);//仅唤醒一个线程函数 pthread_mutex_unlock(mutex); } } pthread_join(ida, NULL); pthread_join(idb, NULL); pthread_mutex_destroy(mutex);//销毁锁 pthread_cond_destroy(cond);//销毁条件变量 exit(0); }wait、signal和broadcast唤醒前都要先加锁。原因保护wait操作的原子性不允许多个线程同时对等待队列进行操作保证同步性进程和线程的区别本质定义进程资源分配的独立单位是程序运行的完整独立容器有独立内存、CPU时间、文件句柄等。线程进程内的执行单位共享所属进程的全部资源只独有栈、寄存器。关键核心差异内存隔离进程完全独立互不干扰一个崩不影响其他。线程共享进程堆/全局内存一个线程出错整个进程易崩溃。开销成本进程创建、销毁、切换开销极大要复刻内存资源。线程轻量创建切换开销极小只改执行上下文。通信方式进程复杂管道、socket、共享内存需系统调用。线程简单直接读写共享全局变量/堆内存即可。资源归属进程独占内存、端口、文件描述符。线程共用进程资源仅私有栈空间。调度层级进程操作系统内核级调度。线程内核/用户态均可调度切换更快。一句话总结进程独立工厂有自己的厂房物料线程工厂里的工人共用厂房物料分工干活。有栈空间。调度层级进程操作系统内核级调度。线程内核/用户态均可调度切换更快。一句话总结进程独立工厂有自己的厂房物料线程工厂里的工人共用厂房物料分工干活。
阅读完成 · 觉得有帮助?
咨询建站