linux----------------------线程同步与互斥(上)
1.线程互斥
1-1 进程线程间的互斥相关背景概念
临界资源:多线程执行流共享的资源就叫做临界资源
临界区:每个线程内部访问临界资源的代码就叫做临界区
互斥:任何时刻,互斥保证只有一个执行进入临界区,对临界资源起到保护作用
原子性:不会被任何调度打断的操作,只有两种状态一个是完成一个是未完成
1-2互斥量mutex
大部分情况线程使用的数据都是局部变量,变量的地址存放在线程栈空间里面,这种情况,变量归属单个线程,其他线程无法获得该变量。有的时候多个线程需要对同一个变量进行操作找个变量称为共享变量,多个线程并发的操作共享变量会带来一些问题
// 操作共享变量会有问题的售票系统代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
int ticket = 100;void *route(void *arg)
{
char *id = (char*)arg;
while ( 1 ) {
if ( ticket > 0 ) {
usleep(1000);
printf("%s sells ticket:%d\n", id, ticket);
ticket--;}
else {break;}
}
}
int main( void )
{pthread_t t1, t2, t3, t4;pthread_create(&t1, NULL, route, "thread 1");pthread_create(&t2, NULL, route, "thread 2");pthread_create(&t3, NULL, route, "thread 3");pthread_create(&t4, NULL, route, "thread 4");pthread_join(t1, NULL);pthread_join(t2, NULL);pthread_join(t3, NULL);pthread_join(t4, NULL);
}
⼀次执⾏结果:
thread 4 sells ticket:100
...
thread 4 sells ticket:1
thread 2 sells ticket:0
thread 1 sells ticket:-1
thread 3 sells ticket:-2
为什么只有100张票到0了的时候还有票能售出去呢?
这就是线程并发执行处理共享数据带来的问题,在我处理最后一张票的时候其他线程也进来了他们看到的都是1此时他们还不知道这个票已经被其他线程给执行完了。如何解决这个问题呢?
代码必须要有互斥行为:当代码进入临界区执行时不允许其他线程进入临界区
如果多个线程同时要求执⾏临界区的代码,并且临界区没有线程在执⾏,那么只能允许⼀个线程
进⼊该临界区。
如果线程不在临界区中执⾏,那么该线程不能阻⽌其他线程进⼊临界区。
要做到这三点只需要在临界区资源上一把锁。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: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);
返回值:成功返回0,失败返回错误号
调用 pthread_lock是,可能会遇到以下情况
互斥量处于未上锁状态,该函数会讲把互斥量锁定返回成功
如何发起函数调用时,其他线程已锁定互斥量,没有竞争到互斥量资源,此时pthread_lock调用会陷入阻塞,直到互斥量解锁
修该上面的售票代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sched.h>
int ticket = 100;
pthread_mutex_t mutex;
void *route(void *arg)
{
char *id = (char*)arg;
while ( 1 ) {
pthread_mutex_lock(&mutex);if ( ticket > 0 ) {
usleep(1000);
printf("%s sells ticket:%d\n", id, ticket);
ticket--;
pthread_mutex_unlock(&mutex);
// sched_yield(); 放弃CPU
}
else {
pthread_mutex_unlock(&mutex);
break;
}}}int main( void )
{
pthread_t t1, t2, t3, t4;
pthread_mutex_init(&mutex, NULL);
pthread_create(&t1, NULL, route, "thread 1");
pthread_create(&t2, NULL, route, "thread 2");
pthread_create(&t3, NULL, route, "thread 3");
pthread_create(&t4, NULL, route, "thread 4");
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_join(t3, NULL);
pthread_join(t4, NULL);
pthread_mutex_destroy(&mutex);
}
1-3 互斥量的封装
test_4_22_httpserve/Mutex.h · liu xi peng/linux---ubuntu系统 - 码云 - 开源中国
大家有兴趣的可以点进去看一下
2.线程同步
2-1 条件变量
当一个线程互斥地访问某个变量时,他可能发现在其他线程状态改变之前他什么也做不了
例如⼀个线程访问队列时,发现队列为空,它只能等待,只到其它线程将⼀个节点添加到队列
中。这种情况就需要⽤到条件变量。
2-2 同步概念与竞态条件
同步:在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从⽽有效避免
饥饿问题,叫做同步
竞态条件:因为时序问题,⽽导致程序异常,我们称之为竞态条件。在线程场景下,这种问题也
不难理解
2-3 条件变量函数
初始化
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t
*restrict attr);
参数:
cond:要初始化的条件变量
attr:NULL
销毁
1 int pthread_cond_destroy(pthread_cond_t *cond)
等待条件满⾜
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict
mutex);
参数:
cond:要在这个条件变量上等待
mutex:互斥量,后⾯详细解释
唤醒等待
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
#include <iostream>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *active( void *arg )
{
std::string name = static_cast<const char*>(arg);
while (true){
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
std::cout << name << " 活动..." << std::endl;
pthread_mutex_unlock(&mutex);
}
}
int main( void )
{
pthread_t t1, t2;
pthread_create(&t1, NULL, active, (void*)"thread-1");
pthread_create(&t2, NULL, active, (void*)"thread-2");
sleep(3); // 可有可⽆,这⾥确保两个线程已经在运⾏
while(true)
{
// 对⽐测试
// pthread_cond_signal(&cond); // 唤醒⼀个线程
pthread_cond_broadcast(&cond); // 唤醒所有线程
sleep(1);
}pthread_join(t1, NULL);
pthread_join(t2, NULL);
}
运行结果:
thread-1 活动...
thread-2 活动...
thread-1 活动...
thread-1 活动...
thread-2 活动...
2-4 ⽣产者消费者模型
321原则(便于记忆)
产消费模型是一种多线程并发的生产模型他有三种关系分别是消费者对消费者,生产者对生产者,消费着对生产者,分别是互斥,互斥,互斥和同步关系
由线程来承担消费者和生产者,线程可有多个,通常有一个特定的数据结构提供的缓冲区这个缓存区通常被成为交易场所
由线程来承担消费者和生产者,线程可有多个,通常有一个特定的数据结构提供的缓冲区这个缓存区通常被成为交易场所