1

假设我有一个线程不断更新某个对象。在更新期间,必须锁定对象以确保线程安全。

现在第二个线程更像是一种事件类型的操作。如果产生了这样的线程,我希望正在运行的更新完成它的调用,然后立即执行事件操作。

我绝对要避免的是事件线程需要等待,直到它幸运地在特定时间获得计算时间,更新线程不会锁定它需要访问的数据。

有什么方法可以使用 c++ 中的线程/互斥工具来完成此任务?还是应该将待完成的操作保存在未锁定的 var 中并在更新线程上执行操作?

//// System.h
#pragma once
#include <mutex>
#include <iostream>
#include <chrono>
#include <thread>

class System {

private:
    int state = 0;
    std::mutex mutex;

public:

    void update();

    void reset(int e);
};
//////// System.cpp
#include "System.h"

void System::update() {
    std::lock_guard<std::mutex> guard(mutex);
    state++;
    std::cout << state << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));
}

void System::reset(int e) {
    std::lock_guard<std::mutex> guard(mutex);
    state = e;
    std::cout << state << std::endl;
}
////// ThreadTest.h
#pragma once

#include <iostream>
#include "System.h"

void loop_update(System& system);

void reset_system(System& system);

int main();
////// ThreadTest.cpp
#include "ThreadTest.h"

void loop_update(System& system) {
    while (true) system.update();
};

void reset_system(System& system) {
    system.reset(0);
};

int main()
{
    System system;
    std::thread t1 = std::thread(loop_update, std::ref(system));

    int reset = 0;

    while (true) {
        
        std::this_thread::sleep_for(std::chrono::seconds(10));
        std::cout << "Reset" << std::endl;
        reset_system(system);
    }
}

示例给出以下输出。您可以清楚地看到实际更新的巨大延迟。

1
...
10
Reset
11
...
16
0
1
...
10
Reset
11
...
43
0
1
4

3 回答 3

1

如果我理解正确,您有 2 个线程使用相同的互斥锁。但是,您希望一个线程获得比另一个线程更高的优先级来获得实际锁定。

据我所知,没有办法确保使用本机工具的偏好。如果您不介意两个线程的代码都知道它,您可以解决它。

例如:

std::atomic<int> shouldPriorityThreadRun{0};

auto priorityThreadCode = [&shouldPriorityThreadRun](){
    ++shouldPriorityThreadRun;
    auto lock = std::unique_lock{mutex};
    doMyStuff();
    --shouldPriorityThreadRun;
};

auto backgroundThreadCode = [&shouldPriorityThreadRun](){
    while (true)
    {
        if (shouldPriorityThreadRun == 0)
        {
           auto lock = std::unique_lock{mutex};
           doBackgroundStuff();
        }
        else
            std::this_thread::yield();
     }
};

如果您有多个优先级线程,则它们之间的优先级不能高于彼此。

如果你不喜欢产量,你可以用 std::condition_variable 做一些更有趣的事情,这样你就可以通知其他线程互斥锁可用。不过,我相信这已经足够好了。

于 2021-07-14T09:28:50.190 回答
0

它应该已经适用于您当前的方法。互斥锁正在锁定对数据的并发访问,因此您可以将其锁定在第一个线程中以更新数据。

如果事件例程/您的第二个线程开始执行,它总是必须检查互斥锁是否已解锁。如果互斥锁被解锁 - 只有这样,您才能锁定互斥锁并执行第二个线程的任务。

如果我正确理解了您的代码(我不是 C++ 专家),则std::lock_guard<std::mutex> guard(mutex);似乎在更新功能的整个过程中都在锁定互斥锁...

因此其他线程只有时间访问互斥锁。

于 2021-07-14T09:21:24.737 回答
-1

update线程完成工作后,它需要在进入睡眠状态之前解锁互斥锁,然后reset线程就有机会立即获得锁。我还尝试在我的机器上运行您的代码并观察它仍在等待锁定。我不知道什么时候能幸运地拿到锁。我认为在这种情况下它是一个UB

2
3
4
5
6
7
8
9
10
Reset
11
12
13
14
15
16
17
18...

void System::update() {
    mutex.lock();
    state++;
    std::cout << state << std::endl;
    mutex.unlock();
    std::this_thread::sleep_for(std::chrono::seconds(1));
}

void System::reset(int e) {
    mutex.lock();
    state = e;
    std::cout << state << std::endl;
    mutex.unlock();
}
于 2021-07-14T09:56:09.010 回答