使用C 中的原始操作员的异步线的相互排除

Mutual exclusion for an asynchronous thread using primitive operators in c++

本文关键字:排除 异步 操作员 原始 使用      更新时间:2023-10-16

我一直在尝试使用c 原语操作员和变量,例如 int if ,而em>要开发螺纹安全机制。

我的想法是使用两个整数变量,称为 sync lock ,增加和检查 sync sync and在此之后,逐步检查锁定。如果所有检查都成功了,则可以保证锁,但是如果检查不成功,它会再次尝试。

看来我的想法在最终验证中断言。

#include <assert.h>
#include <iostream>
#include <string>
#include <thread>
#include <vector>
class Resource {
    // Shared resource to be thread safe.
    int resource;
    // Mutual exclusion variables.
    volatile int lock;
    volatile int sync;
    public:
        Resource() : resource( 0 ), lock( 0 ), sync( 0 ) {}
        ~Resource() {}
        int sharedResource() {
            return resource;
        }
        void sharedResourceAction( std::string id ) {
            bool done;
            do {
                int oldSync = sync;
                // ++ should be atomic.
                sync++;
                if ( sync == oldSync + 1 ) {
                    // ++ should be atomic.
                    lock++;
                    if ( lock == 1 ) {
                        // For the sake of the example, the read-modify-write
                        // is not atomic and not thread safe if threre is no
                        // mutex surronding it.
                        int oldResource = resource;
                        resource = oldResource + 1;
                        done = true;
                    }
                    // -- should be atomic.
                    lock--;
                }
                if ( !done ) {
                    // Pseudo randomic sleep to unlock the race condition
                    // between the threads.
                    std::this_thread::sleep_for(
                             std::chrono::microseconds( resource % 5 ) );
                }
            } while( !done );
        }
};
static const int maxThreads = 10;
static const int maxThreadActions = 1000;
void threadAction( Resource& resource, std::string& name ) {
    for ( int i = 0; i < maxThreadActions; i++) {
        resource.sharedResourceAction( name );
    }
}
int main() {
    std::vector< std::thread* > threadVec;
    Resource resource;
    // Create the threads.
    for (int i = 0; i < maxThreads; ++i) {
        std::string name = "t";
        name += std::to_string( i );
        std::thread *thread = new std::thread( threadAction,
                                               std::ref( resource ),
                                               std::ref( name ) );
        threadVec.push_back( thread );
    }
    // Join the threads.
    for ( auto threadVecIter = threadVec.begin();
          threadVecIter != threadVec.end(); threadVecIter++ ) {
        (*threadVecIter)->join();
    }
    std::cout << "Shared resource is " << resource.sharedResource()
              << std::endl;
    assert( resource.sharedResource() == ( maxThreads * maxThreadActions ) );
    return 0;
}

是否存在仅使用原始变量和操作员保护共享资源的线程安全机制?

no,有几个原因为什么不起作用

首先,标准描述了它不起作用。您(明确(有一个读/写和写入/写入比赛条件,并且标准禁止此。

其次,++i绝不是原子。即使在主流英特尔处理器上,也不是 - 当需要是lock inc指令时,通常是inc指令。

第三,挥发性在C 中没有螺纹含义,就像Java或C#中一样。它既不是必要的也不是足够的来实现与螺纹安全有关的任何事情(在挥发性的volatile:/ms之类的令人讨厌的编译器扩展程序之外(。有关C 挥发性的更多信息,请参见此答案。

您的代码中可能还有更多问题,但此列表应该足以劝阻您。

编辑:为了实际回答您的最后一个问题 - 不,我认为以标准的方式实现原始类型和操作的线程安全机制是不可能的。基本上,您需要获得内存 - 辅助,CPU和编译器都同意在实施线程安全机制时不要执行某些转换。通常,这意味着您需要使用编译器挂钩或在标准之外保证,并且还需要了解最终目标CPU的保证或内在的实现。

volatile绝对不适合多线程:

在执行线程中,通过 挥发性的glvalues不能重新排序,而不是可观察到的副作用 (包括其他挥发性访问(,以之前进行测序或 在同一线程中进行了测序,但此顺序不是 由于挥发性访问,保证可以通过另一个线程观察到 不建立线程间同步。

此外,挥发性 访问不是原子质(并发读写是数据竞赛(和 请勿订购内存(非易失性内存访问可能是自由的 在挥发性访问中重新排序(。

如果您想在整数上进行原子操作,则适当的方法是使用std::atomic<int>。这使您可以保证其他线程会观察到的内存排序。如果您真的想执行这种无锁的编程,则应坐下并吸收内存模型文档,如果您像我一样,请重新考虑尝试在试图阻止头部爆炸时尝试无锁的编程。