网站设计与网站开发是同时进行的做儿童成长相册模版网站
news/
2025/10/7 18:07:04/
文章来源:
网站设计与网站开发是同时进行的,做儿童成长相册模版网站,网站开发亿玛酷技术,线上托管2 线程同步
线程同步中的“同步”与生活中大家认知的“同步”略有不同#xff0c;“同”不指同时#xff0c;其主旨在于协同步调#xff0c;按预定的先后次序执行线程#xff1b;之所以需要实现线程同步#xff0c;是因为若不对线程的执行次序加以控制#xff0c;可能会…2 线程同步
线程同步中的“同步”与生活中大家认知的“同步”略有不同“同”不指同时其主旨在于协同步调按预定的先后次序执行线程之所以需要实现线程同步是因为若不对线程的执行次序加以控制可能会出现数据混乱。
出现与事件有关的错误的原因有三个
1资源共享2调度随机3线程间缺乏必要的同步机制。
Linux系统实现线程同步的方式常用的有三种
互斥锁条件变量信号量。
2.1 互斥锁
使用互斥锁的实现线程同步时主要操作分为四步 ①初始化互斥锁pthread_mutex_init ②加锁pthread_mutex_lock ③解锁pthread_mutex_unlock ④销毁锁pthread_mutxe_destroy
2.1.1 初始化互斥锁
#include pthread.hint pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
功能初始化互斥锁。
参数说明
mutex一个传入传出参数 – ①pthread_mutext_t的本质是结构体为简化理解读者可将其视为整型 – ②变量mutex只有两种取值0和1加锁mutex-1解锁mutex1 – ③参数mutex之前的restrict是一个关键字该关键字用于限制指针功能是告诉编译器所有修改该指针指向内容的操作只能通过本指针完成。attr一个传入传出参数代表互斥量的属性通常传NULL表示使用默认属性。
返回值说明
成功返回0不成功返回errnoerrno的常见取值为EAGAIN和EDEADLK其中EAGAIN表示超出互斥锁递归锁定的最大次数因此无法获取该互斥锁EDEADLK表示当前线程已有互斥锁二次加锁失败。
2.1.2 加锁
#include pthread.hint pthread_mutex_lock(pthread_mutex_t *mutex);功能锁定指定互斥量。
参数说明
mutex待锁定的互斥量。
返回值说明
成功返回0不成功返回errno。
#include pthread.hint pthread_mutex_trylock(pthread_mutex_t *mutex);功能尝试加锁若锁正在被使用不阻塞等待而是直接返回并返回错误号。
参数说明
mutex待锁定的互斥量。
返回值说明
成功返回0不成功返回errno其中常见的errno有两个分别为EBUSY和EAGAIN它们代表的含义如下 – EBUSY参数mutex指向的互斥锁已锁定 – EAGAIN超过互斥锁递归锁定的最大次数。
2.1.3 解锁
#include pthread.hint pthread_mutex_unlock(pthread_mutex_t *mutex);功能为指定互斥量解锁。
参数说明
mutex待解锁的互斥量。
返回值说明
成功返回0不成功返回errno。
2.1.4 销毁锁
#include pthread.hint pthread_mutex_destroy(pthread_mutex_t *mutex);功能为指定互斥量销毁。
参数说明
mutex待销毁的互斥量。
返回值说明
成功返回0不成功返回errno。
【案例 1】在原线程和新线程中分别进行打印操作使原线程分别打印“HELLO”、“ WORLD”新线程分别打印“hello”、“world”。
//未添加mutex
#include stdio.h
#include pthread.h
#include unistd.h
void *tfn(void *paraArg) {srand(time(NULL));while (1) {printf(hello );//模拟长时间操作共享资源导致cpu易主产生与时间有关的错误sleep(rand() % 3);printf(world\n);sleep(rand() % 3);}//of whilereturn NULL;
}//of tfn
int main(void) {pthread_t tempTid;srand(time(NULL));pthread_create(tempTid, NULL, tfn, NULL);while (1) {printf(HELLO );sleep(rand() % 3);printf(WORLD\n);sleep(rand() % 3);}//of whilepthread_join(tempTid, NULL);return 0;
}//of main未添加互斥量会导致打印乱序。
#include stdio.h
#include string.h
#include pthread.h
#include stdlib.h
#include unistd.h
pthread_mutex_t globMutux; //定义互斥锁
void err_thread(int paraRet, char *paraStr) {if (paraRet ! 0) {fprintf(stderr, %s:%s\n, paraStr, strerror(paraRet));pthread_exit(NULL);}//of if
}//of err_thread
void *tfn(void *paraArg) {srand(time(NULL));while (1) {pthread_mutex_lock(globMutux); //加锁m--printf(hello );//模拟长时间操作共享资源导致cpu易主产生与时间有关的错误sleep(rand() % 3);printf(world\n);pthread_mutex_unlock(globMutux); //解锁msleep(rand() % 3);}//of whilereturn NULL;
}//of tfn
int main(void) {pthread_t tempTid;srand(time(NULL));int tempFlag 5;pthread_mutex_init(globMutux, NULL); //初始化mutexm1int tempRet pthread_create(tempTid, NULL, tfn, NULL);err_thread(tempRet, pthread_create error);while (tempFlag--) {pthread_mutex_lock(globMutux); //加锁m--printf(HELLO );sleep(rand() % 3);printf(WORLD\n);pthread_mutex_unlock(globMutux); //解锁m--sleep(rand() % 3);}//of whilepthread_cancel(tempTid);pthread_join(tempTid, NULL);pthread_mutex_destroy(globMutux);return 0;
}//of main2.2 条件变量
使用条件变量控制线程同步时线程访问共享资源的前提是程序中设置的条件变量得到满足。条件变量不会对共享资源加锁但也会使线程阻塞若线程不满足条件变量规定的条件就会进入阻塞状态直到条件满足。 条件变量的使用分为以下四个步骤
1初始化条件变量pthread_cond_init()2等待条件变量满足pthread_cond_wait()3唤醒阻塞线程pthread_cond_signal()、pthread_cond_broadcast()4释放条件变量pthread_cond_destroy()。
2.2.1 初始化条件变量
#include pthread.hint pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);功能初始化条件变量。
参数说明
cond代表条件变量一个指向pthread_cond_t的结构体指针pthread_cond_t是Linux系统定义的条件变量类型attr代表条件变量的属性 – NULL表示使用默认属性初始化条件变量 – PTHREAD_PROCESS_PRIVATE表示当前进程中的线程共用此条件变量 – PTHREAD_PROCESS_SHARED表示多个进程间的线程共用条件变量。
返回值说明
成功返回0不成功返回-1并设置errno。
2.2.2 等待条件变量满足
#include pthread.hint pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);功能使线程进入阻塞状态等待一个条件变量后继续执行。pthread_cond_wait类似于互斥锁的pthread_mutex_lock函数但其功能更为丰富它的工作机制如下
1阻塞等待条件变量cond满足2解除已绑定的互斥锁类似于pthread_mutex_unlock3当线程被唤醒pthread_cond_wait函数返回pthread_cond_wait函数同时会解除线程阻塞并使线程重新申请绑定互斥锁。 条件变量控制流程示意图
参数说明
cond代表条件变量一个指向pthread_cond_t的结构体指针pthread_cond_t是Linux系统定义的条件变量类型mutex代表与当前线程绑定的互斥锁。
返回值说明
成功返回0不成功返回-1并设置errno。
#include pthread.hint pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,const struct timespec *restrict abstime);
struct timespec {time_t tv_sec; //秒long tv_nsec; //纳秒
};功能使线程阻塞等待条件变量不同的是该函数可以指定线程的阻塞时长若等待超时该函数便会返回。
参数说明
cond代表条件变量一个指向pthread_cond_t的结构体指针pthread_cond_t是Linux系统定义的条件变量类型mutex代表与当前线程绑定的互斥锁;abstime等待时长。
返回值说明
成功返回0不成功返回-1并设置errno。
2.2.3 唤醒条件变量
#include pthread.hint pthread_cond_signal(pthread_cond_t *cond);功能在条件变量满足之后以信号的形式唤醒阻塞在该条件变量的一个线程。处于阻塞状态中的线程的唤醒顺序由调度策略决定。
参数说明
cond代表条件变量一个指向pthread_cond_t的结构体指针pthread_cond_t是Linux系统定义的条件变量类型。
返回值说明
成功返回0不成功返回-1并设置errno。
#include pthread.hint pthread_cond_broadcast(pthread_cond_t *cond);功能唤醒阻塞在指定条件变量的线程不同的是该函数会以广播的形式唤醒阻塞在该条件变量上的所有线程。
参数说明
cond代表条件变量一个指向pthread_cond_t的结构体指针pthread_cond_t是Linux系统定义的条件变量类型。
返回值说明
成功返回0不成功返回-1并设置errno。
2.2.4 销毁条件变量
#include pthread.hint pthread_cond_destory(pthread_cond_t *cond);功能当没有线程在等待参数cond指定的条件变量时才可以销毁条件变量。
参数说明
cond代表条件变量一个指向pthread_cond_t的结构体指针pthread_cond_t是Linux系统定义的条件变量类型。
返回值说明
成功返回0不成功返回EBUSY。
【案例2】生产者-消费者模型是线程同步中的一个经典案例。假设有两个线程这两个线程同时操作一个共享资源一般称为汇聚其中一个模拟生产者行为生产共享资源当容器存满时生产者无法向其中放入产品另一个线程模拟消费者行为消费共享资源当产品数量为0时消费者无法获取产品应阻塞等待。显然为防止数据混乱每次只能由生产者、消费者中的一个操作共享资源。本案例要求使用程序实现简单的生产者-消费者模型可假设容器无限大
#include stdio.h
#include stdlib.h
#include unistd.h
#include pthread.h
struct msg {struct msg *next;int num;
};
struct msg *globHead;
pthread_cond_t globHasProduct PTHREAD_COND_INITIALIZER; //初始化条件变量
pthread_mutex_t globLock PTHREAD_MUTEX_INITIALIZER; //初始化互斥锁
//消费者
void *consumer(void *paraP) {struct msg *tempMsgP;for (;;) {pthread_mutex_lock(globLock); //加锁//若头结点为空表明产品数量为0消费者无法消费产品while (globHead NULL) {pthread_cond_wait(globHasProduct, globLock); //阻塞等待并解锁}//of whiletempMsgP globHead;globHead tempMsgP-next; //模拟消费一个产品pthread_mutex_unlock(globLock);printf(-Consume ---%d\n, tempMsgP-num);free(tempMsgP);sleep(rand() % 5);}//of for
}//of consumer
//生产者
void *producer(void *paraP) {struct msg *tempMsgP;while (1) {tempMsgP malloc(sizeof(struct msg));tempMsgP-num rand() % 1000 1; //模拟生产一个产品printf(-Produce ---%d\n, tempMsgP-num);pthread_mutex_lock(globLock); //加锁tempMsgP-next globHead; //插入结点添加产品globHead tempMsgP;pthread_mutex_unlock(globLock); //解锁pthread_cond_signal(globHasProduct); //唤醒等待在该条件变量上的一个线程sleep(rand() % 5);}//of while
}//of producer
int main(int argc, char *argv[]) {pthread_t tempPid, tempCid;srand(time(NULL));//创建生产者、消费者线程pthread_create(tempPid, NULL, producer, NULL);pthread_create(tempCid, NULL, consumer, NULL);//回收线程pthread_join(tempPid, NULL);pthread_join(tempCid, NULL);return 0;
}//of main2.3 信号量
使用信号量实现线程同步时线程在访问共享资源时会根据操作类型执行如下操作
若有线程申请访问共享资源系统会执行P操作使共享资源计数减一若有线程释放共享资源系统会执行V操作使共享资源计数加一。
信号量的使用也分为四个步骤
1初始化信号量sem_init()2阻塞等待信号量sem_wait()3唤醒阻塞线程sem_post()4释放信号量sem_destroy()。
2.3.1 初始化信号量
#include pthread.hint sem_init(sem_t *sem, int pshared, unsigned int value);功能初始化信号量。
参数说明
sem指向信号量变量的指针;pshared用于控制信号量的作用范围其取值通常为0与非0 – 当pshared被设置为0时信号量将会被放在进程中所有线程可见的地址内由进程中的线程共享 – 当pshared被设置为非0值时信号量将会被放置在共享内存区域由所有进程共享。value设置信号量sem的初值。
返回值说明
成功返回0不成功返回-1并设置errno。
2.3.2 阻塞等待信号量
#include pthread.hint sem_wait(sem_t *sem);功能阻塞等待信号量sem_wait函数对应P操作若调用成功则会使信号量sem的值减一。
参数说明
sem指向信号量变量的指针。
返回值说明
成功返回0不成功返回-1并设置errno。
2.3.3 唤醒阻塞线程
#include pthread.hint sem_post(sem_t *sem);功能唤醒阻塞线程sem_post函数对应V操作若调用成功则会使信号量sem的值加一。
参数说明
sem指向信号量变量的指针。
返回值说明
成功返回0不成功返回-1并设置errno。
2.3.4 释放信号量
#include pthread.hint sem_destroy(sem_t *sem);功能与互斥锁类似信号量也是一种系统资源使用完毕之后应主动回收函数调用成功则会使信号量sem的值加一。
参数说明
sem指向信号量变量的指针。
返回值说明
成功返回0不成功返回-1并设置errno。
2.3.5 获取信号量
#include pthread.hint sem_getvalue(sem_t *sem, int *sval);功能获取系统中当前信号量的值。
参数说明
sem指向信号量变量的指针sval个传入指针用于获取信号量的值信号量sem的值会被存储在参数sval中。
返回值说明
成功返回0不成功返回-1并设置errno。
【案例 3】实现一个模拟生产者-消费者模型但对生产者进行限制若容器已满生产者不能生产需等待消费者消费。
#include stdlib.h
#include unistd.h
#include pthread.h
#include stdio.h
#include semaphore.h
#define NUM 5
int globQueue[NUM]; //全局数组实现环形队列
sem_t globBlankNum, globProductNum; //空格子信号量, 产品信号量
void *producer(void *paraArg) {int i 0;while (1) {sem_wait(globBlankNum); //生产者将空格子数--,为0则阻塞等待globQueue[i] rand() % 1000 1; //生产一个产品printf(----Produce---%d\n, globQueue[i]);sem_post(globProductNum); //将产品数i (i 1) % NUM; //借助下标实现环形sleep(rand() % 1);}//of while
}//of producer
void *consumer(void *paraArg) {int i 0;while (1) {sem_wait(globProductNum); //消费者将产品数--,为0则阻塞等待printf(-Consume---%d\t%lu\n, globQueue[i], pthread_self());globQueue[i] 0; //消费一个产品 sem_post(globBlankNum); //消费掉以后,将空格子数i (i 1) % NUM;sleep(rand() % 1);}//of while
}//of consumer
int main(int paraArgc, char *paraArgv[]) {pthread_t tempPid, tempCid;sem_init(globBlankNum, 0, NUM); //初始化空格子信号量为5sem_init(globProductNum, 0, 0); //初始化产品数信号量为0pthread_create(tempPid, NULL, producer, NULL);pthread_create(tempCid, NULL, consumer, NULL);pthread_create(tempCid, NULL, consumer, NULL);pthread_join(tempPid, NULL);pthread_join(tempCid, NULL);sem_destroy(globBlankNum);sem_destroy(globProductNum);return 0;
}//of main2.4 小结
本部分主要讲解了Linux系统中与线程相关的知识包括线程相关操作及线程同步其中线程操作包括创建线程、退出线程、终止线程、回收线程等线程同步包括互斥锁、条件变量、信号量这线程同步的三种方式。线程是Linux编程基础中非常重要的一项内容。
2.5 编程题
【1】编写一个程序开启三个线程第一个线程向终端输出A第二个线程向终端输出B第三个线程向终端输出C每个线程打印10遍要求输出必须按照ABC的顺序显示如ABCABCABC… 【2】利用线程的信号量实现互斥功能模拟打印机。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/930693.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!