C++多线程std::lock

lock 类

std::lock_guard,与 mutex RAII 相关,方便线程对互斥量上锁。
std::unique_lock,与 mutex RAII 相关,方便线程对互斥量上锁,但提供了更好的上锁和解锁控制。

std::lock_guard

在定义时构造函数中会lock,析构函数会自动unlock。

使用了lock_guard后就不应该对mutex再使用lock或unlock了。

#include <stdio.h>
#include <stdlib.h>

#include <iostream> // std::cout
#include <thread>   // std::thread
#include <mutex>    // std::mutex

int a = 0;
std::mutex mtx;

void thread_task1()
{
    for (int i = 0; i < 100000; ++i)
    {
        std::lock_guard<std::mutex> lock(mtx);
        //mtx.lock();
        ++a;
        //mtx.unlock();
    }
}

void thread_task2()
{
    for (int i = 0; i < 100000; ++i)
    {
        std::lock_guard<std::mutex> lock(mtx);
        //mtx.lock();
        ++a;
        //mtx.unlock();
    }
}

int main()
{
    std::thread t1(thread_task1);
    std::thread t2(thread_task2);
    t1.join();
    t2.join();
    std::cout << a << std::endl;
    return 0;
}
其他类型

std::once_flag
std::adopt_lock_t
std::defer_lock_t
std::try_to_lock_t

std::lock函数

可以同时对多个互斥量上锁如果互斥量中有一个没锁住,那么它会解锁已锁住的互斥量,不断尝试同时锁住所有的互斥量,所以这个函数的结果要么是所有互斥量都锁住了要么时所有互斥量一个都没锁住。
比如C++多线程std::mutex中最后讲到的死锁问题,就可以用这个函数解决。

#include <stdio.h>
#include <stdlib.h>

#include <iostream> // std::cout
#include <thread>   // std::thread
#include <mutex>    // std::mutex

int a = 0;
std::mutex mtx1;
std::mutex mtx2;

void thread_task1()
{
    for (int i = 0; i < 100000; ++i)
    {
        std::lock(mtx1, mtx2);
        //mtx1.lock();
        //mtx2.lock();
        ++a;
        mtx2.unlock();
        mtx1.unlock();
    }
}

void thread_task2()
{
    for (int i = 0; i < 100000; ++i)
    {
        std::lock(mtx1, mtx2);
        //mtx1.lock();
        //mtx2.lock();
        ++a;
        mtx1.unlock();
        mtx2.unlock();
    }
}

int main()
{
    std::thread t1(thread_task1);
    std::thread t2(thread_task2);
    t1.join();
    t2.join();
    std::cout << a << std::endl;
    return 0;
}

但是std::lock锁住的锁不会自动释放锁。
下面介绍std::lock_guard的std::adopt_lock参数。

std::adopt_lock参数

此参数作用就是让std::lock_guard在构造函数中不调用mutex的lock函数。
此参数也可用于std::unique::lock,作用一样。

#include <stdio.h>
#include <stdlib.h>

#include <iostream> // std::cout
#include <thread>   // std::thread
#include <mutex>    // std::mutex

int a = 0;
std::mutex mtx1;
std::mutex mtx2;

void thread_task1()
{
    for (int i = 0; i < 100000; ++i)
    {
        std::lock(mtx1, mtx2);
        std::lock_guard<std::mutex> lock1(mtx1, std::adopt_lock);
        std::lock_guard<std::mutex> lock2(mtx2, std::adopt_lock);
        ++a;
    }
}

void thread_task2()
{
    for (int i = 0; i < 100000; ++i)
    {
        std::lock(mtx1, mtx2);
        std::lock_guard<std::mutex> lock1(mtx1, std::adopt_lock);
        std::lock_guard<std::mutex> lock2(mtx2, std::adopt_lock);
        ++a;
    }
}

int main()
{
    std::thread t1(thread_task1);
    std::thread t2(thread_task2);
    t1.join();
    t2.join();
    std::cout << a << std::endl;
    return 0;
}

std::unique_lock

与 Mutex RAII 相关,方便线程对互斥量上锁,但提供了更好的上锁和解锁控制,它的功能比std::lock_guard更多,摩尔嗯不带参数的std::unique_lock与默认不带参数的std::lock_guard功能基本一致。
std::unique_lock使用std::adopt_lock参数前,互斥锁必须是已经被本线程锁定的。
使用std::try_to_lock参数前,互斥锁必须没有被本线程锁定的,后续可以owns_lock()函数判断是否已经获取到了锁的所有权。
使用std::defer_lock参数前,互斥锁必须没有被本线程锁定的,同时也不在构造函数中对锁进行锁定,使用这个参数定义的std::unique_lock,后续可以使用std::unique_lock的成员函数lock、unlock、try_lock,最后可以不解锁也可以解锁,不解锁的话析构仍会自动解锁。
release()函数会释放锁的所有权,而不是解锁,返回指向锁的指针。

#include <stdio.h>
#include <stdlib.h>

#include <iostream> // std::cout
#include <thread>   // std::thread
#include <mutex>    // std::mutex

int a = 0;
std::mutex mtx;

void thread_task1()
{
    for (int i = 0; i < 100000; ++i)
    {
        std::unique_lock<std::mutex> lock(mtx);
        ++a;
    }
}

void thread_task2()
{
    for (int i = 0; i < 100000; ++i)
    {
        std::unique_lock<std::mutex> lock(mtx);
        ++a;
    }
}

int main()
{
    std::thread t1(thread_task1);
    std::thread t2(thread_task2);
    t1.join();
    t2.join();
    std::cout << a << std::endl;
    return 0;
}

std::try_lock函数

尝试同时对多个互斥量上锁,注意与std::unique_lock的std::try_to_lock参数区分。

std::call_once函数

如果多个线程需要同时调用某个函数,call_once 可以保证多个线程对该函数只调用一次。需要配合std::one_flag类使用。

#include <stdio.h>
#include <stdlib.h>

#include <iostream> // std::cout
#include <thread>   // std::thread
#include <mutex>    // std::mutex

int a = 0;
std::once_flag g_flag;

void thread_task1()
{
    ++a;
}

void thread_task2()
{
    std::call_once(g_flag, thread_task1);
}

int main()
{
    std::thread t1(thread_task2);
    std::thread t2(thread_task2);
    t1.join();
    t2.join();
    std::cout << a << std::endl;
    return 0;
}

当有多个线程调用的某个函数中有func,可以采用上述方法让该函数中的func只被执行一次。
thread_task2函数虽然被执行了两次,但是thread_task1只被执行了一次。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
std::atomic_bool是C++14中的原子布尔类型。它提供了原子操作,以确保多线程环境下的安全性。它可以用于实现锁、同步和并发算法等。使用std::atomic_bool可以保证对布尔类型的读写操作在多线程环境中是原子的,即不会发生竞态条件。 在C++14中,std::atomic_bool比std::atomic_flag功能更全,可以使用非原子的bool来赋值和初始化。例如,你可以这样使用std::atomic_bool: std::atomic_bool b(true); b = false; 这样就可以对b进行原子的赋值操作。 需要注意的是,std::atomic_flag由于限制性甚至不能用作一个通用的布尔标识,所以最好还是使用std::atomic_bool。在C语言中,也可以使用自旋锁来实现多线程的同步。以下是一个使用自旋锁实现的例子: #include <thread> #include <vector> #include <iostream> #include <atomic> std::atomic_flag lock = ATOMIC_FLAG_INIT; void f(int n) { for (int cnt = 0; cnt < 5; cnt++) { while (lock.test_and_set(std::memory_order_acquire)) ; // 自旋 std::cout << "Thread " << n << " count:" << cnt << std::endl; lock.clear(std::memory_order_release); // 释放锁 } } int main(int argc, char* argv[]) { std::vector<std::thread> v; for (int n = 0; n < 4; n++) { v.emplace_back(f, n); //使用参数进行初始化 } for (auto& t : v) { t.join(); //等待线程结束 } system("pause"); return 0; } 这个例子中,使用std::atomic_flag作为锁,通过test_and_set()和clear()函数来获得锁和释放锁。 总结来说,std::atomic_bool是C++14中的原子布尔类型,可以用于多线程环境下的安全操作。在C语言中,可以使用自旋锁来实现多线程的同步。<span class="em">1</span><span class="em">2</span><span class="em">3</span>

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值