大学生网站建设与网页设计报告,购物网站界面设计策划,太原新站优化,什么是网站模板设计文章目录 #x1f4dd;线程互斥#x1f320; 库函数strncpy#x1f309;进程线程间的互斥相关背景概念#x1f309;互斥量mutex #x1f320;线程同步#x1f309;条件变量#x1f309;同步概念与竞态条件#x1f309; 条件变量函数 #x1f6a9;总结 #x1f4dd;线… 文章目录 线程互斥 库函数strncpy进程线程间的互斥相关背景概念互斥量mutex 线程同步条件变量同步概念与竞态条件 条件变量函数 总结 线程互斥 库函数strncpy
进程线程间的互斥相关背景概念
临界资源多线程执⾏流共享的资源就叫做临界资源临界区每个线程内部访问临界资源的代码就叫做临界区互斥任何时刻互斥保证有且只有⼀个执⾏流进⼊临界区访问临界资源通常对临界资源起保护作⽤原⼦性后⾯讨论如何实现不会被任何调度机制打断的操作该操作只有两态要么完成要么未完成
互斥量mutex
⼤部分情况线程使⽤的数据都是局部变量变量的地址空间在线程栈空间内这种情况变量归属单个线程其他线程⽆法获得这种变量。但有时候很多变量都需要在线程间共享这样的变量称为共享变量可以通过数据的共享完成线程之间的交互。多个线程并发的操作共享变量会带来⼀些问题。
Makefile文件
binticket
ccg
src$(wildcard *.cc)
obj$(src:.cc.o)$(bin):$(obj)$(cc) -o $ $^ -lpthread
%.o:%.ccecho Comiling $ to $$(cc) -c $ -stdc17.PHONY:clean
clean:rm -f $(bin) $(obj).PHONY:test
test:echo $(src)echo $(obj)代码
#include stdio.h
#include string
#include string.h
#include pthread.h
#include unistd.hint ticket 100;void* routine(void* args)
{char *id (char*)args;// std::string id static_castconst char*(args);while(1){if(ticket 0){usleep(10000);printf(%s sells ticket:%d\n, id, ticket);ticket--;}else{break;}}return nullptr;
}int main(void)
{pthread_t t1, t2 , t3, t4;pthread_create(t1, nullptr, routine, (void*)thread 1);pthread_create(t2, nullptr, routine, (void*)thread 2);pthread_create(t3, nullptr, routine, (void*)thread 3);pthread_create(t4, nullptr, routine, (void*)thread 4);pthread_join(t1, nullptr);pthread_join(t2, nullptr);pthread_join(t3, nullptr);pthread_join(t4, nullptr);return 0;
}if语句判断条件为真以后代码可以并发的切换到其他进程usleep这个模拟夜漫长业务的过程这个漫长的业务过程中可能有多个线程会进入该代码段--ticket操作本身就不是一个原子操作
取出ticket–部分的汇编代码
objdump -d a.out test.objdump152 40064b: 8b 05 e3 04 20 00 mov 0x2004e3(%rip),%eax
600b34 ticket153 400651: 83 e8 01 sub $0x1,%eax
154 400654: 89 05 da 04 20 00 mov %eax,0x2004da(%rip)
600b34 ticket--操作并不是原子操作而是对应三条汇编指令:
load将共享变量体的从内存加载到寄存器update更新寄存器里面的只执行复议操作store:将新值从寄存器写回共享变量ticket的内存地址
要解决以上问题需要做到三点
代码必须要有互斥行为当代码进入临界区执行时不允许其他进程进入该临界区如果多个线程同时要求进入临界区的代码并且临界区没有线程在执行那么只能一个线程进入该临界区如果现场不在临界区中执行那么该现场就不能阻止其他进程进入临界区
要做到这三点本身是上就是需要一把锁linux上提供这把锁叫互斥量 互斥量的接口 初始化互斥量的两种方法
方法1静态分配
pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER ⽅法2动态分配: int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); 参数 mutex要初始化的互斥量 attr这是一个指向 pthread_mutexattr_t 类型对象的指针该类型用于定义互斥锁的属性。如果将其设置为 NULL
销毁互斥量 销毁互斥量需要注意
使用PTHREAD_ MUTEX_ INITIALIZER初始化的互斥量不需要销毁不要销毁⼀个已经加锁的互斥量已经销毁的互斥量要确保后⾯不会有线程再尝试加锁
int pthread_mutex_destroy(pthread_mutex_t *mutex)互斥量加锁和解锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
返回值:成功返回o,失败返回错误号调用pthread_ lock时可能会遇到以下情况:
互斥量处于未锁状态该函数会将互斥量锁定同时返回成功发起函数调用时其他线程已经锁定互斥量或者存在其他线程同时申请互斥量但没有竞争到互斥量那么pthread_lock调用会陷入阻塞(执行流被挂起)等待互斥量解锁。
改进上面的售票系统:
#include stdio.h
#include string
#include string.h
#include pthread.h
#include unistd.hint ticket 100;
pthread_mutex_t mutex;void* routine(void* args)
{char *id (char*)args;// std::string id static_castconst char*(args);while(1){pthread_mutex_lock(mutex);if(ticket 0){usleep(1000);printf(%s sells ticket:%d\n, id, ticket);ticket--;pthread_mutex_unlock(mutex);}else{pthread_mutex_unlock(mutex);break;}}return nullptr;
}int main(void)
{pthread_t t1, t2 , t3, t4;pthread_create(t1, nullptr, routine, (void*)thread 1);pthread_create(t2, nullptr, routine, (void*)thread 2);pthread_create(t3, nullptr, routine, (void*)thread 3);pthread_create(t4, nullptr, routine, (void*)thread 4);pthread_join(t1, nullptr);pthread_join(t2, nullptr);pthread_join(t3, nullptr);pthread_join(t4, nullptr);pthread_mutex_destroy(mutex);return 0;
}RAII风格的互斥锁C11也有比如: std : : mutex mtx; std : : lock_guard guard ( mtx ) ; 线程同步
条件变量
当⼀个线程互斥地访问某个变量时它可能发现在其它线程改变状态之前它什么也做不了。例如⼀个线程访问队列时发现队列为空它只能等待只到其它线程将⼀个节点添加到队列中。这种情况就需要⽤到条件变量。
同步概念与竞态条件
同步在保证数据安全的前提下让线程能够按照某种特定的顺序访问临界资源从⽽有效避免饥饿问题叫做同步竞态条件因为时序问题⽽导致程序异常我们称之为竞态条件。在线程场景下这种问题也 不难理解 条件变量函数
初始化
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t*restrict attr);参数: cond:要初始化的条件变量 attr: NULL
销毁
int pthread_cond_destroy(pthread_cond_t *cond)等待条件满⾜
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrictmutex);
参数:
cond:要在这个条件变量上等待
mutex:互斥量后面详细解释唤醒等待
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);简单案例:
我们先使用PTHREAD_COND/MUTEX_INITIALIZER进行测试对其他细节暂不追究然后将接口更改成为使用pthread_cond_init/pthread_cond_destroy的方式方便后续进行封装
#include iostream
#include string.h
#include unistd.h
#include pthread.hpthread_cond_t cond PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER;void* active(void* args)
{std::string name static_castconst char*(args);while(true){pthread_mutex_lock(mutex);pthread_cond_wait(cond, mutex);std::cout name 活动... std::endl;pthread_mutex_unlock(mutex);}
}int main()
{pthread_t t1, t2;pthread_create(t1, nullptr, active, (void*)thread -1);pthread_create(t2, nullptr, active, (void*)thread -2);sleep(3);while(true){//对比测试pthread_cond_signal(cond);//唤醒一个线程// pthread_cond_broadcast(cond);//唤醒所有线程sleep(1);}pthread_join(t1, nullptr);pthread_join(t2, nullptr);pthread_cond_destroy(cond);pthread_mutex_destroy(mutex);return 0;
}总结