c++之多线程中“锁”(mutex)

 

从c++11开始,c++提供了std::mutex类型,对于多线程的加锁操作提供了很好的支持。

Demo1——无锁的情况

假定有一个全局变量counter,启动两个线程,每个都对该变量自增10000次,最后输出该变量的值。在第一个demo中,我们不加锁,代码文件保存为:mutex_demo1_no_mutex.cpp

#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
#include <chrono>
#include <stdexcept>

int counter = 0;
void increase(int time) {
    for (int i = 0; i < time; i++) {
        // 当前线程休眠1毫秒
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
        counter++;
    }
}

int main(int argc, char** argv) {
    std::thread t1(increase, 10000);
    std::thread t2(increase, 10000);
    t1.join();
    t2.join();
    std::cout << "counter:" << counter << std::endl;
    return 0;
}
g++ mutex_demo1_no_mutex.cpp -o no_mutex -lpthread

结果:

结果不是20000

root@ubuntu:/app/boost/example/mutex-demo# ./no_mutex
counter:19981
root@ubuntu:/app/boost/example/mutex-demo# ./no_mutex
counter:19983

出现上述情况的原因是:自增操作"counter++"不是原子操作,而是由多条汇编指令完成的。多个线程对同一个变量进行读写操作就会出现不可预期的操作。以上面的demo1作为例子:假定counter当前值为10,线程1读取到了10,线程2也读取到了10,分别执行自增操作,线程1和线程2分别将自增的结果写回counter,不管写入的顺序如何,counter都会是11,但是线程1和线程2分别执行了一次自增操作,我们期望的结果是12

 

加锁mutex

mutex_demo2_with_mutex.cpp

#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
#include <chrono>
#include <stdexcept>

int counter = 0;
std::mutex mtx; // 保护counter

void increase(int time) {
    for (int i = 0; i < time; i++) {
        mtx.lock();
        // 当前线程休眠1毫秒
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
        counter++;
        mtx.unlock();
    }
}

int main(int argc, char** argv) {
    std::thread t1(increase, 10000);
    std::thread t2(increase, 10000);
    t1.join();
    t2.join();
    std::cout << "counter:" << counter << std::endl;
    return 0;
}

结果正确:

root@ubuntu:/app/boost/example/mutex-demo# ./with_mutex
counter:20000
root@ubuntu:/app/boost/example/mutex-demo# ./with_mutex
counter:20000

避免死锁

mutex_demo4_lock_guard.cpp

#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
#include <chrono>
#include <stdexcept>

int counter = 0;
std::mutex mtx; // 保护counter

void increase_proxy(int time, int id) {
    for (int i = 0; i < time; i++) {
        // std::lock_guard对象构造时,自动调用mtx.lock()进行上锁
        // std::lock_guard对象析构时,自动调用mtx.unlock()释放锁
        std::lock_guard<std::mutex> lk(mtx);
        // 线程1上锁成功后,抛出异常:未释放锁
        if (id == 1) {
            throw std::runtime_error("throw excption....");
        }
        // 当前线程休眠1毫秒
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
        counter++;
    }
}

void increase(int time, int id) {
    try {
        increase_proxy(time, id);
    }
    catch (const std::exception& e){
        std::cout << "id:" << id << ", " << e.what() << std::endl;
    }
}

int main(int argc, char** argv) {
    std::thread t1(increase, 10000, 1);
    std::thread t2(increase, 10000, 2);
    t1.join();
    t2.join();
    std::cout << "counter:" << counter << std::endl;
    return 0;
}

运行结果:

root@ubuntu:/app/boost/example/mutex-demo# ./lock_guard
id:1, throw excption....
counter:10000

 

改进版本,去掉可恶的全局变量

work.h


#pragma once

#include <mutex>
namespace application
{
        class Worker
        {
        public:

        Worker();
        ~Worker();
        void increase_proxy(int time, int id);
        void increase(int time, int id);
        int get_count(){ return count_;}

        private:
        int                     count_;
        std::mutex              mutex_;

        };

}

 

worker.cpp

#include <iostream>
#include <thread>
#include <vector>
//#include <mutex>
#include <chrono>
#include <stdexcept>
#include "worker.h"

//std::mutex mtx;
namespace application
{
    Worker::Worker()
        : count_()
    {

    }

    Worker::~Worker()
    {

    }

    void Worker::increase_proxy(int time, int id) {
    for (int i = 0; i < time; i++) {
        std::lock_guard<std::mutex> lk(mutex_);
        if (id == 1) {
//            throw std::runtime_error("throw excption....");
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
        count_++;
    }
}

void Worker::increase(int time, int id) {
    try {
        increase_proxy(time, id);
    }
    catch (const std::exception& e){
        std::cout << "id:" << id << ", " << e.what() << std::endl;
    }
}
}

int main(int argc, char** argv) {
    application::Worker* wk = new application::Worker();
    std::thread t1(&application::Worker::increase, wk, 10000, 1);
    std::thread t2(&application::Worker::increase, wk, 10000, 2);
    t1.join();
    t2.join();
    std::cout << "counter:" << wk->get_count() << std::endl;
    delete wk;
    return 0;
}

结果:

root@ubuntu:/app/boost/example/mutex-demo# g++ worker.cpp -o worker -lpthread
root@ubuntu:/app/boost/example/mutex-demo# ./worker
counter:20000

 

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页