1.什么是互斥量(锁)?
互斥量在实际开发中很常用,需要学习了解!
这样比喻:单位上有一台打印机(共享数据a),你要用打印机(线程1要操作数据a),同事老王也要用打印机(线程2也要操作数据a),但是打印机同一时间只能给一个人用,此时,规定不管是谁,在用打印机之前都要向领导申请许可证(lock),用完后再向领导归还许可证(unlock),许可证总共只有一个,没有许可证的人就等着在用打印机的同事用完后才能申请许可证(阻塞,线程1lock互斥量后其他线程就无法lock,只能等线程1unlock后,其他线程才能lock)。那么,打印机就是共享数据,访问打印机的这段代码就是临界区,这个必须互斥使用的许可证就是互斥量(锁)。
互斥量是为了解决数据共享过程中可能存在的访问冲突的问题。这里的互斥量保证了使用打印机这一过程不被打断。
2.死锁
死锁在实际开发中会遇到,需要学习了解!
多线程编程时要考虑多个线程同时访问共享资源所造成的问题,因此可以通过加锁解锁来保证同一时刻只有一个线程能访问共享资源;使用锁的时候要注意,不能出现死锁的状况;
死锁就是多个线程争夺共享资源导致每个线程都不能取得自己所需的全部资源,从而程序无法向下执行。
3.例子代码
对于不同线程访问统一资源时,为了避免冲突一般都通过对目标共享变量上锁和解锁,让共享变量互斥。
3.1 第一种方式:一般情况可以在共享变量前后分别上锁解锁,至少需要以下三个操作:
// 定义锁
std::mutex m_mutex;
// 上锁
m_mutex.lock();
// 上锁和解锁之间为对共享变量的访问操作.....
// 解锁
m_mutex.unlock();
3.2 第二种方式:使用std::lock_guard,在std::lock_guard对象的作用域内进行互斥量的操作,例如:
// std::lock_guard<std::mutex> l(s_mutex);
#include <iostream>
#include <mutex>
#include <thread>
#include <windows.h>
//全局变量,两个线程都会访问
int g_num = 0;
//定义锁
std::mutex m_mutex;
void bar()
{
//函数bar()范围内,自动为互斥量上锁和解锁
std::lock_guard<std::mutex> l(m_mutex);
std::cout << "This thread id is : " << std::this_thread::get_id() << " -- g_num : " << g_num << std::endl;
g_num++;
}
void foo()
{
while (1) {
bar();
Sleep(1000);
}
}
int main()
{
std::thread first(foo); // thread first
std::thread second(foo); // thread second
first.join(); // pauses until first finishes
second.join(); // pauses until second finishes
return 0;
}
std::lock_guard需要在作用域范围开头定义,也可以通过块操作限制其作用域范围,例如:
void func()
{
....
{ // 范围起始
std::lock_guard<std::mutex> l(m_mutex);
} // 范围结束
....
}
4.参考例子
#include "lock_guard.hpp"
#include <iostream>
#include <thread>
#include <mutex>
#include <stdexcept>
#include <list>
#include <algorithm>
namespace lock_guard_ {
///
// reference: http://www.cplusplus.com/reference/mutex/lock_guard/
namespace {
std::mutex mtx;
void print_even(int x) {
if (x % 2 == 0) std::cout << x << " is even\n";
else throw (std::logic_error("not even"));
}
void print_thread_id(int id) {
try {
// using a local lock_guard to lock mtx guarantees unlocking on destruction / exception:
std::lock_guard<std::mutex> lck(mtx);
print_even(id);
} catch (std::logic_error&) {
std::cout << "[exception caught]\n";
}
}
}
int test_lock_guard_1()
{
std::thread threads[10];
// spawn 10 threads:
for (int i = 0; i<10; ++i)
threads[i] = std::thread(print_thread_id, i + 1);
for (auto& th : threads) th.join();
return 0;
}
///
// reference: http://www.cplusplus.com/reference/mutex/lock_guard/lock_guard/
namespace {
std::mutex mtx2; // mutex for critical section
void print_thread_id2(int id) {
mtx2.lock();
std::lock_guard<std::mutex> lck(mtx2, std::adopt_lock);
std::cout << "thread #" << id << '\n';
}
}
int test_lock_guard_2()
{
std::thread threads[10];
// spawn 10 threads:
for (int i = 0; i<10; ++i)
threads[i] = std::thread(print_thread_id2, i + 1);
for (auto& th : threads) th.join();
return 0;
}
// reference: http://en.cppreference.com/w/cpp/thread/lock_guard
namespace {
int g_i = 0;
std::mutex g_i_mutex; // protects g_i
void safe_increment()
{
std::lock_guard<std::mutex> lock(g_i_mutex);
++g_i;
std::cout << std::this_thread::get_id() << ": " << g_i << '\n';
// g_i_mutex is automatically released when lock goes out of scope
}
}
int test_lock_guard_3()
{
std::cout << "main: " << g_i << '\n';
std::thread t1(safe_increment);
std::thread t2(safe_increment);
t1.join();
t2.join();
std::cout << "main: " << g_i << '\n';
return 0;
}
//
// reference: http://www.bogotobogo.com/cplusplus/C11/7_C11_Thread_Sharing_Memory.php
namespace {
// a global variable
std::list<int> myList;
// a global instance of std::mutex to protect global variable
std::mutex myMutex;
void addToList(int max, int interval)
{
// the access to this function is mutually exclusive
std::lock_guard<std::mutex> guard(myMutex);
for (int i = 0; i < max; i++) {
if ((i % interval) == 0) myList.push_back(i);
}
}
void printList()
{
// the access to this function is mutually exclusive
std::lock_guard<std::mutex> guard(myMutex);
for (auto itr = myList.begin(), end_itr = myList.end(); itr != end_itr; ++itr) {
std::cout << *itr << ",";
}
}
}
int test_lock_guard_4()
{
int max = 100;
std::thread t1(addToList, max, 1);
std::thread t2(addToList, max, 10);
std::thread t3(printList);
t1.join();
t2.join();
t3.join();
return 0;
}
} // namespace lock_guard_