c++ 如何组织这个多线程应用程序,使它不会陷入“等待”锁的困境?

1szpjjfi  于 2023-05-02  发布在  其他
关注(0)|答案(1)|浏览(98)

(更新:我有一些好主意,尝试在答案和评论-要开始尝试的东西,这周和更新时,再次工作!)
这是一个我已经挣扎了一段时间。我有一个C++类,它有许多公共函数,并且期望这些函数中的每一个都将定期从不同的线程调用。类的基本形式是这样的:

class DataProcessor
{
public:
    void process1(...);
    void process2(...);
    void process3(...);
    // etc...

    void update();
};

这是一个机器人应用程序,每个“进程”函数都用于接收来自不同传感器的数据,因此每当新的传感器数据进来时,我们都会在自己的线程中调用相应的“进程”函数。“update”函数在一个循环中使用,看起来像这样:

while(true) {
   wait 1 second;
   dataProcessor.update();
}

现在,我已经设置好了,这样每个“进程”函数都可以被并发调用而没有任何问题,但是“更新”函数不应该在任何“进程”函数执行时被调用。更新函数相当快,几乎不需要任何时间就能执行,而一些进程函数比较昂贵,可能会达到0的数量级。1-0.2秒。
到目前为止,我已经尝试了两种策略来使其安全执行,但这两种策略都导致函数花费太多时间阻塞/等待互斥锁(“互斥锁”?)解锁。
我能做的第一件事是让每个“进程”函数在执行的开始和结束时锁定和解锁自己的互斥体,然后“更新”函数必须拥有所有互斥体才能执行。当我这样做时,我会遇到以下问题:
1.“process 1”函数完成并解锁互斥锁1

  1. update函数锁定mutex 1,并等待mutex 2被解锁
    1.再次调用“procees 1”函数,但必须等待update函数解锁mutex 1
  2. update函数在完成执行之前不能解锁mutex 1,因为它正在等待所有其他“进程”函数完成
    因此,现在“process 1”函数实际上必须等待所有其他“process”函数完成,即使这些进程函数被设计为能够并发运行。
    所以另一个策略是使用计数器。基本上是一个“numberOfFunctionsRunning”变量,因此每个“process”函数可以在开始时递增变量,并在结束时递减。然后update函数只能在变量为零时执行。这稍微好一点,因为现在流程函数可以再次并发执行,但现在更新函数永远等待,因为变量很少达到零。
    所以,是的,我希望这是足够的信息,这是有意义的,我想做的事情。有没有什么线程策略可以用来确保“进程”函数可以执行而不需要花费大量的时间等待,同时还可以确保更新函数可以定期执行?
yqlxgs2m

yqlxgs2m1#

您可以使用std::lock以原子方式锁定所有互斥锁,如下所示:

class DataProcessor
{
public:
    void process1()
    {
        std::unique_lock<std::mutex> lock(m1);

        // do stuff here
    }

    void process2()
    {
        std::unique_lock<std::mutex> lock(m2);

        // do stuff here
    }

    void update()
    {
        // use std::defer_lock to hold off the locking
        std::unique_lock<std::mutex> lock1(m1, std::defer_lock);
        std::unique_lock<std::mutex> lock2(m2, std::defer_lock);

        std::lock(lock1, lock2); // lock all mutexes atomically

        // do stuff here
    }

private:
    std::mutex m1;
    std::mutex m2;
    std::mutex m3;
};

这样,void update()中的锁只有在其他函数都没有持有锁时才会生效,从而避免了死锁。

相关问题