繁体   English   中英

在地图中使用std :: thread :: id来确保线程安全

[英]use std::thread::id in map to have thread safe

这个想法是每个线程都有实例,所以我为每个新thread::id创建了新实例:

struct doSomething{
    void test(int toto) {}
};

void test(int toto)
{
    static std::map<std::thread::id, doSomething *> maps;

    std::map<std::thread::id, doSomething *>::iterator it = maps.find(std::this_thread::get_id());
    if (it == maps.end())
    {
        // mutex.lock() ?
        maps[std::this_thread::get_id()] = new doSomething();
        it = maps.find(std::this_thread::get_id());
        // mutex.unlock() ?
    }
    it->second->test(toto);
}

这是个好主意吗?

不,不是一个好主意。

std::map的方法本身不是线程安全的。

为了真正使它成为一个“好主意”,您还必须使用互斥量或等效方法使对std::map所有访问都线程安全。

这不仅包括您已注释掉的部分,还包括您正在使用的所有其他方法,例如find ()。

触摸std::map所有内容都必须是互斥保护的。

在访问地图后拥有互斥锁是不够的。 没有互斥锁,您无法走到地图附近的任何地方,因为在您从地图读取数据时,另一个线程可能会使用互斥锁来修改地图。

{
    std::unique_lock<std::mutex> lock(my_mutex);
    std::map<std::thread::id, doSomething *>::iterator it = maps.find(std::this_thread::get_id());
    if (it != maps.end())
        return *it;
    auto ptr = std::make_unique<doSomething>();
    maps[std::this_thread::get_id()] = ptr.get();
    return ptr.release();
}

但是,除非您有一些特殊/独特的用例,否则这是通过线程本地存储解决的问题,并且由于您具有C ++ 11,因此具有thread_local存储说明符

请注意,我在这里使用mutex ,因为cout是共享资源,并且yield只是为了鼓励工作流的更多交错。

#include <iostream>
#include <memory>
#include <thread>
#include <mutex>

static std::mutex cout_mutex;

struct CoutGuard : public std::unique_lock<std::mutex> {
    CoutGuard() : unique_lock(cout_mutex) {}
};

struct doSomething {
    void fn() {
        CoutGuard guard;
        std::cout << std::this_thread::get_id() << " running doSomething "
            << (void*)this << "\n";
    }
};

thread_local std::unique_ptr<doSomething> tls_dsptr; // DoSomethingPoinTeR

void testFn() {
    doSomething* dsp = tls_dsptr.get();
    if (dsp == nullptr) {
        tls_dsptr = std::make_unique<doSomething>();
        dsp = tls_dsptr.get();
        CoutGuard guard;
        std::cout << std::this_thread::get_id() << " allocated "
            << (void*)dsp << "\n";
    } else {
        CoutGuard guard;
        std::cout << std::this_thread::get_id() << " re-use\n";
    }
    dsp->fn();
    std::this_thread::yield();
}

void thread_fn() {
    testFn();
    testFn();
    testFn();
}

int main() {
    std::thread t1(thread_fn);
    std::thread t2(thread_fn);
    t2.join();
    t1.join();
}

现场演示: http : //coliru.stacked-crooked.com/a/3dec7efcb0018549

g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
140551597459200 allocated 0x7fd4a80008e0
140551597459200 running doSomething 0x7fd4a80008e0
140551605851904 allocated 0x7fd4b00008e0
140551605851904 running doSomething 0x7fd4b00008e0
140551605851904 re-use
140551605851904 running doSomething 0x7fd4b00008e0
140551597459200 re-use
140551605851904 re-use
140551597459200 running doSomething 0x7fd4a80008e0
140551605851904 running doSomething 0x7fd4b00008e0
140551597459200 re-use
140551597459200 running doSomething 0x7fd4a80008e0

很难发现,但是线程'9200分配了..4a80 ..,而线程'1904分配了..4b00..。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM