【Linux】多线程服务端编程

条件竞争

  • 在即将析构一个对象时,从何而知此刻是否有别的线程正在执行该对象的成员函数?
  • 如何保证在执行成员函数期间,对象不会在另一个线程被析构?
  • 在调用某个对象的成员函数之前,如何得知这个对象还活着?它的析构函数会不会碰巧执行到一半?

线程安全类的定义

  • 多个线程同时访问时,其表现出正确的行为。
  • 无论操作系统如何调度这些线程,无论这些线程的执行顺序如何交织(interleaving)。
  • 调用端代码无须额外的同步或其他协调动作。

创建类线程安全

安全的构造函数
  1. 不要在构造函数中注册任何回调
  2. 不要把this传递给其他对象
  3. 即使是最后一行也不能(派生类会先执行基类的构造函数,会导致实际上执行到最后一行,对象仍然还是个半成品)
    一定要注册回调的话,使用二段式构造,创建新的init函数,由调用方来执行注册
安全的析构函数

很困难,没有办法任何办法保证。只能从使用角度,在析构的时候,没有任何一个人去使用类的成员函数来防止该问题。

安全的swap函数

一个函数如果要锁住相同类型的多个对象,为了保证始终按相同的顺序加锁,我们可以比较mutex对象的地址,始终先加锁地址较小的mutex。
当前c++11已经内置方法实现。

// 赋值操作符
Counter& Counter::operator=(const Counter& rhs) {
    if (this == &rhs)
        return *this;

    // 锁定两个互斥锁,确保以相同的顺序获取锁,避免死锁
    std::scoped_lock lock(mutex_, rhs.mutex_);

    value_ = rhs.value_;
    return *this;
}

// 获取当前值
int64_t Counter::value() const {
    MutexLockGuard lock(mutex_);
    return value_;
}

// 获取当前值并自增
int64_t Counter::getAndIncrease() {
    MutexLockGuard lock(mutex_);
    int64_t ret = value_++;
    return ret;
}

使用智能指针加锁场景

当多个线程同时访问并修改同一个std::shared_ptr实例(例如,重置指针、增加或减少引用计数)时,需要加锁以防止数据竞争。
例如:

std::shared_ptr<int> ptr = std::make_shared<int>(10);
std::mutex mtx;

// Thread 1
{
    std::lock_guard<std::mutex> lock(mtx);
    ptr.reset(new int(20));
}

// Thread 2
{
    std::lock_guard<std::mutex> lock(mtx);
    ptr = std::make_shared<int>(30);
}
什么时候不需要加锁
  • 局部 std::shared_ptr 实例:如果 std::shared_ptr 实例是局部的,并且不在多个线程之间共享,则不需要加锁。
  • 只读访问:如果 std::shared_ptr 实例在多个线程中只读访问(每个线程只读取指向的对象,而不修改它),则不需要加锁,因为读取操作是线程安全的。

尽量减小临界区

排除一些IO操作,比如构造和析构函数。

要将globalPtr原来指向的Foo对象的销毁行为移出临界区,你可以使用一个临时的 shared_ptr 来保存 globalPtr 的旧值,并在临界区外进行销毁。以下是修改后的 write 函数:

void write()
{
    std::shared_ptr<Foo> newPtr(new Foo); // 注意,对象的创建在临界区之外
    
    std::shared_ptr<Foo> oldPtr; // 临时的shared_ptr,用于保存旧值
    {
        MutexLockGuard lock(mutex);
        oldPtr = globalPtr; // 保存旧值
        globalPtr = newPtr;  // 更新globalPtr
    } // 离开临界区

    // oldPtr 在这里离开作用域,原来的 Foo 对象会在这里销毁
    doit(newPtr);
}

在这个实现中,oldPtr 被用来保存 globalPtr 的旧值,而旧值的销毁行为发生在临界区之外。这确保了临界区内只进行必要的指针交换操作,而不涉及可能的对象销毁,从而提高了临界区的效率和安全性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ooo胡大侠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值