12.线程同步与互斥
/临界资源//锁本身是全局的,那么锁也是共享资源//加锁//1.抢票//解锁elsebreak;int main()//1.构建线程对象i < NUM;i++)//2.启动线程//3.等待线程return 0;但是锁本身就是临界资源,那么谁保证锁的安全呢?加锁和解锁被设计成为原子的(本身就是原子的)如何看待锁呢?二元信号量就是锁!加锁的本质就是对资源展开预定!整体使用资源!如果申请锁的时候,锁被别
一.上集回顾
建议先学上篇博客,再向下学习,上篇博客的链接如下:
二.线程互斥


1.快速见一见,没有加保护,会有什么问题
#include <iostream>
#include "Thread.hpp"
#include <vector>
using namespace ThreadModule;
#define NUM 4
int ticketnum = 10000;//临界资源
void Ticket()
{
while(true)
{
if(ticketnum > 0)
{
usleep(1000);
//1.抢票
printf("get a new ticket, id: %d\n",ticketnum--);
}
else
{
break;
}
}
}
int main()
{
//1.构建线程对象
std::vector<Thread> threads;
for(int i = 0;i < NUM;i++)
{
threads.emplace_back(Ticket);
}
//2.启动线程
for(auto& thread:threads)
{
thread.Start();
}
//3.等待线程
for(auto& thread:threads)
{
thread.Join();
}
return 0;
}


代码为啥会出现这个问题呢?
![]()


ticketnum--这一条指令,就会被转换成为三条汇编



所以,这个ticketnum--不是原子性的(ticketnum > 0 也不是原子的)






![]()
![]()
要么是一行汇编,要么是多行代码,但是加锁保护
2.解决方案

所有对资源的保护,都是对临界区代码的保护,因为资源是通过代码访问的!

3.接口介绍
a.锁的初始化




b.加锁





4.改进售票系统
a.加默认锁
![]()
#include <iostream>
#include "Thread.hpp"
#include <vector>
using namespace ThreadModule;
#define NUM 4
int ticketnum = 10000;//临界资源
//锁本身是全局的,那么锁也是共享资源
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
void Ticket()
{
while(true)
{
pthread_mutex_lock(&lock);//加锁
if(ticketnum > 0)
{
usleep(1000);
//1.抢票
printf("get a new ticket, id: %d\n",ticketnum--);
pthread_mutex_unlock(&lock);//解锁
}
else
{
pthread_mutex_unlock(&lock);
break;
}
}
}
int main()
{
//1.构建线程对象
std::vector<Thread> threads;
for(int i = 0;i < NUM;i++)
{
threads.emplace_back(Ticket);
}
//2.启动线程
for(auto& thread:threads)
{
thread.Start();
}
//3.等待线程
for(auto& thread:threads)
{
thread.Join();
}
return 0;
}

但是锁本身就是临界资源,那么谁保证锁的安全呢?
加锁和解锁被设计成为原子的(本身就是原子的)
如何看待锁呢?
二元信号量就是锁! 加锁的本质就是对资源展开预定! 整体使用资源!
如果申请锁的时候,锁被别人已经拿走了,怎么办?
其他线程要进行阻塞等待
线程在访问临界区代码的时候,可不可以切换?
可以切换! 我被切换的时候,别人没办法进入,我是抱着锁,被切换的!!(串行,这就是效率低的原因)
![]()
b.局部锁
#include <iostream>
#include "Thread.hpp"
#include <vector>
using namespace ThreadModule;
#define NUM 4
int ticketnum = 10000;//临界资源
class ThreadData
{
public:
std::string name;
pthread_mutex_t* lock_ptr;
};
void Ticket(ThreadData& td)
{
while(true)
{
pthread_mutex_lock(td.lock_ptr);//加锁
if(ticketnum > 0)
{
usleep(1000);
//1.抢票
printf("get a new ticket, who get it: %s, id: %d\n",td.name.c_str(),ticketnum--);
pthread_mutex_unlock(td.lock_ptr);//解锁
}
else
{
pthread_mutex_unlock(td.lock_ptr);
break;
}
}
}
int main()
{
pthread_mutex_t lock;
pthread_mutex_init(&lock,nullptr);
//1.构建线程对象
std::vector<Thread<ThreadData>> threads;
for(int i = 0;i < NUM;i++)
{
ThreadData* td = new ThreadData();
td->lock_ptr = &lock;
threads.emplace_back(Ticket,*td);
td->name = threads.back().Name();
}
//2.启动线程
for(auto& thread:threads)
{
thread.Start();
}
//3.等待线程
for(auto& thread:threads)
{
thread.Join();
}
pthread_mutex_destroy(&lock);
return 0;
}

#include <iostream>
#include "Thread.hpp"
#include <vector>
using namespace ThreadModule;
#define NUM 4
int ticketnum = 10000;//临界资源
class ThreadData
{
public:
std::string name;
pthread_mutex_t* lock_ptr;
};
void Ticket(ThreadData& td)
{
while(true)
{
pthread_mutex_lock(td.lock_ptr);//加锁
if(ticketnum > 0)
{
usleep(1000);
//1.抢票
printf("get a new ticket, who get it: %s, id: %d\n",td.name.c_str(),ticketnum--);
pthread_mutex_unlock(td.lock_ptr);//解锁
//入库模拟操作
usleep(50);
}
else
{
pthread_mutex_unlock(td.lock_ptr);
break;
}
}
}
int main()
{
pthread_mutex_t lock;
pthread_mutex_init(&lock,nullptr);
//1.构建线程对象
std::vector<Thread<ThreadData>> threads;
for(int i = 0;i < NUM;i++)
{
ThreadData* td = new ThreadData();
td->lock_ptr = &lock;
threads.emplace_back(Ticket,*td);
td->name = threads.back().Name();
}
//2.启动线程
for(auto& thread:threads)
{
thread.Start();
}
//3.等待线程
for(auto& thread:threads)
{
thread.Join();
}
pthread_mutex_destroy(&lock);
return 0;
}


三.锁的如何实现的?





所以,这个1就是我们的锁,这里就是exchange指令通过1条汇编,就完成了加锁的操作

四.封装锁
1.C++锁的使用
#include <iostream>
#include <mutex>
#include <vector>
using namespace ThreadModule;
#define NUM 4
int ticketnum = 10000;//临界资源
std::mutex gmutex;
class ThreadData
{
public:
std::string name;
pthread_mutex_t* lock_ptr;
};
void Ticket(ThreadData& td)
{
while(true)
{
gmutex.lock();
if(ticketnum > 0)
{
usleep(1000);
//1.抢票
printf("get a new ticket, who get it: %s, id: %d\n",td.name.c_str(),ticketnum--);
gmutex.unlock();
//入库模拟操作
usleep(50);
}
else
{
gmutex.unlock();
break;
}
}
}
int main()
{
pthread_mutex_t lock;
pthread_mutex_init(&lock,nullptr);
//1.构建线程对象
std::vector<Thread<ThreadData>> threads;
for(int i = 0;i < NUM;i++)
{
ThreadData* td = new ThreadData();
td->lock_ptr = &lock;
threads.emplace_back(Ticket,*td);
td->name = threads.back().Name();
}
//2.启动线程
for(auto& thread:threads)
{
thread.Start();
}
//3.等待线程
for(auto& thread:threads)
{
thread.Join();
}
pthread_mutex_destroy(&lock);
return 0;
}

2.封装mutex
a.基本的初始化和销毁
#pragma once
#include <iostream>
#include <pthread.h>
namespace LockModule
{
class Mutex
{
public:
Mutex()
{
int n = ::pthread_mutex_init(&_lock,nullptr);
(void)n;
}
~Mutex()
{
int n = ::pthread_mutex_destroy(&_lock);
(void)n;
}
private:
pthread_mutex_t _lock;
};
};
b.加锁和解锁
#pragma once
#include <iostream>
#include <pthread.h>
namespace LockModule
{
class Mutex
{
public:
Mutex()
{
int n = ::pthread_mutex_init(&_lock,nullptr);
(void)n;
}
void Lock()
{
int n = ::pthread_mutex_lock(&_lock);
(void)n;
}
void Unlock()
{
int n = ::pthread_mutex_unlock(&_lock);
(void)n;
}
~Mutex()
{
int n = ::pthread_mutex_destroy(&_lock);
(void)n;
}
private:
pthread_mutex_t _lock;
};
};
c.禁用拷贝和赋值
#pragma once
#include <iostream>
#include <pthread.h>
namespace LockModule
{
class Mutex
{
public:
Mutex(const Mutex&) = delete;
const Mutex& operator=(const Mutex&) = delete;
Mutex()
{
int n = ::pthread_mutex_init(&_lock,nullptr);
(void)n;
}
void Lock()
{
int n = ::pthread_mutex_lock(&_lock);
(void)n;
}
void Unlock()
{
int n = ::pthread_mutex_unlock(&_lock);
(void)n;
}
~Mutex()
{
int n = ::pthread_mutex_destroy(&_lock);
(void)n;
}
private:
pthread_mutex_t _lock;
};
};
d.LockGuard类的设计
#pragma once
#include <iostream>
#include <pthread.h>
namespace LockModule
{
class Mutex
{
public:
Mutex(const Mutex&) = delete;
const Mutex& operator=(const Mutex&) = delete;
Mutex()
{
int n = ::pthread_mutex_init(&_lock,nullptr);
(void)n;
}
void Lock()
{
int n = ::pthread_mutex_lock(&_lock);
(void)n;
}
void Unlock()
{
int n = ::pthread_mutex_unlock(&_lock);
(void)n;
}
~Mutex()
{
int n = ::pthread_mutex_destroy(&_lock);
(void)n;
}
private:
pthread_mutex_t _lock;
};
class LockGuard
{
public:
LockGuard(Mutex& mtx):_mtx(mtx)
{
_mtx.Lock();
}
~LockGuard()
{
_mtx.Unlock();
}
private:
Mutex& _mtx;
};
};
e.锁的使用
原来的代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sched.h>
int ticket = 1000;
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);
} else {
pthread_mutex_unlock(&mutex);
break;
}
}
return nullptr;
}
int main(void)
{
pthread_t t1, t2, t3, t4;
pthread_mutex_init(&mutex, NULL);
pthread_create(&t1, NULL, route, (void*)"thread_1");
pthread_create(&t2, NULL, route, (void*)"thread_2");
pthread_create(&t3, NULL, route, (void*)"thread_3");
pthread_create(&t4, NULL, route, (void*)"thread_4");
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_join(t3, NULL);
pthread_join(t4, NULL);
pthread_mutex_destroy(&mutex);
return 0;
}

换我们自己的锁:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include "Mutex.hpp"
using namespace LockModule;
int ticket = 1000;
Mutex mtx;
void *route(void *arg)
{
char *id = (char*)arg;
while (1) {
mtx.Lock();
if (ticket > 0) {
usleep(1000);
printf("%s sells ticket:%d\n", id, ticket);
ticket--;
mtx.Unlock();
} else {
mtx.Unlock();
break;
}
}
return nullptr;
}
int main(void)
{
pthread_t t1, t2, t3, t4;
pthread_create(&t1, NULL, route, (void*)"thread_1");
pthread_create(&t2, NULL, route, (void*)"thread_2");
pthread_create(&t3, NULL, route, (void*)"thread_3");
pthread_create(&t4, NULL, route, (void*)"thread_4");
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_join(t3, NULL);
pthread_join(t4, NULL);
return 0;
}

但是这还是得我们自己进行加锁和解锁,还是太麻烦了,我们这里采用RAII的加锁方式
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include "Mutex.hpp"
using namespace LockModule;
int ticket = 1000;
Mutex mtx;
void *route(void *arg)
{
char *id = (char*)arg;
while (1) {
{
LockGuard lockguard(mtx);
if (ticket > 0) {
usleep(1000);
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, NULL, route, (void*)"thread_1");
pthread_create(&t2, NULL, route, (void*)"thread_2");
pthread_create(&t3, NULL, route, (void*)"thread_3");
pthread_create(&t4, NULL, route, (void*)"thread_4");
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_join(t3, NULL);
pthread_join(t4, NULL);
return 0;
}

五.线程同步




1.条件变量接口



signal是唤醒一个线程,broadcast是唤醒全部线程
2.理解条件变量

敲一下铃铛就相当于是唤醒一个人,broadcast就是唤醒所有的线程

3.写一下demo代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sched.h>
int ticket = 0;
pthread_mutex_t mutex;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
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);
} else {
printf("%s wait on cond!\n",id);
pthread_cond_wait(&cond,&mutex);//醒来的时候,会重新申请锁!!
printf("%s 被叫醒了\n",id);
}
pthread_mutex_unlock(&mutex);
}
return nullptr;
}
int main(void)
{
pthread_t t1, t2, t3, t4;
pthread_mutex_init(&mutex, NULL);
pthread_create(&t1, NULL, route, (void*)"thread_1");
pthread_create(&t2, NULL, route, (void*)"thread_2");
pthread_create(&t3, NULL, route, (void*)"thread_3");
pthread_create(&t4, NULL, route, (void*)"thread_4");
int cnt = 10;
while(true)
{
sleep(5);
ticket += cnt;
printf("主线程放票咯! ticket: %d\n",ticket);
pthread_cond_signal(&cond);
}
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_join(t3, NULL);
pthread_join(t4, NULL);
pthread_mutex_destroy(&mutex);
return 0;
}

更多推荐


所有评论(0)