愿意在64位指针值上引入竞争条件

Bor*_*rov 7 c++ x86 multithreading race-condition undefined-behavior

我有一个应用程序对象,它可以从运行在多个线程中的多个服务接收消息。该消息由服务线程中的调度程序对象的实例在内部进行调度。该应用程序可以随时更改当前调度程序。调度员永远不会被摧毁。服务永远不会超过应用程序。

这是一个示例代码

#include <iostream>
#include <thread>
#include <atomic>
#include <cstdlib>
#include <functional>

using namespace std;

using Msg = int;

struct Dispatcher
{
    virtual ~Dispatcher() = default;
    virtual void dispatchMessage(Msg msg) = 0;
};

struct DispatcherA : Dispatcher
{
    void dispatchMessage(Msg msg)
    {
        cout << "Thread-safe dispatch of " << msg << " by A" << endl;
    }
};

struct DispatcherB : Dispatcher
{
    void dispatchMessage(Msg msg)
    {
        cout << "Thread-safe dispatch of " << msg << " by B" << endl;
    }
};

struct Application
{
    Application() : curDispatcher(&a) {}
    void sendMessage(Msg msg)
    {
        // race here as this is called (and dereferenced) from many threads
        // and can be changed by the main thread
        curDispatcher->dispatchMessage(msg);
    }

    void changeDispatcher()
    {
        // race her as this is changed but can be dereferenced by many threads
        if (rand() % 2) curDispatcher = &a;
        else curDispatcher = &b;
    }

    atomic_bool running = true;

    Dispatcher* curDispatcher; // race on this
    DispatcherA a;
    DispatcherB b;
};

void service(Application& app, int i) {
    while (app.running) app.sendMessage(i++);
}

int main()
{
    Application app;
    std::thread t1(std::bind(service, std::ref(app), 1));
    std::thread t2(std::bind(service, std::ref(app), 20));

    for (int i = 0; i < 10000; ++i) 
    {
        app.changeDispatcher();
    }
    app.running = false;

    t1.join();
    t2.join();
    return 0;
}

Run Code Online (Sandbox Code Playgroud)

我知道这里有比赛条件。该curDispatcher指针被多个线程访问,它可以在同一时间由主线程来改变。可以通过使指针原子化并在每次sendMessage调用时显式加载它来修复该问题。

我不想付出原子负载的代价。

会发生不好的事情吗?

这是我能想到的:

  • 的值curDispatcher可以由服务缓存,即使应用程序更改了值,它也可以始终调用相同的值。我可以。如果我不再对此表示满意,则可以使其变得不稳定。无论如何,新创建的服务应该可以。
  • 如果此程序在模拟64位的32位CPU上运行,则该指针的写入和读取将不是指令级原子的,并且可能导致无效的指针值并崩溃:我正在确保仅在以下位置运行64位CPU。
  • 破坏调度员并不安全。正如我所说:我从不破坏调度员。
  • ???