添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
I created a class to try out std::lock_guard.
The first function, UnsafeExample compiles and runs fine. The second example, SafeExample does not compile and a bunch of errors are thrown.
I have a hunch that it might have to do std::move(t) leaving t in an empty state but maybe not. Anyway, I want to have a mapping of int to threads (why not) instead of the usual vector.
1
2
3
4
5
6
7
8
9
10
11
struct SafeThreadCoutExample
    const int numThreads = -1;
    std::map<int, std::thread> threadHandles;
    std::mutex coutMutex;
    SafeThreadCoutExample(int nThreads) :  numThreads(nThreads) {}
    void UnsafeExample(void);
    void SafeExample(void);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
void SafeThreadCoutExample::UnsafeExample(void)
    // Create nThreads
    for (int i = 0; i < numThreads; i++)
        threadHandles.emplace(std::make_pair(i, thread{ Task_PrintHelloWithIdAndExit, i }));
    // Wait for all threads to finish
    for (int i = 0; i < numThreads; i++)
        if (threadHandles[i].joinable())
            threadHandles[i].join();
            cout << "Thread-" << i << " NOT joinable!\n";
void SafeThreadCoutExample::SafeExample(void)
    // Create nThreads
    for (int i = 0; i < numThreads; i++)
        thread t(Task_Safe_PrintHelloWithIdAndExit, i, coutMutex);
        threadHandles.emplace(std::make_pair(i, std::move(t)));
    // Wait for all threads to finish
    for (int i = 0; i < numThreads; i++)
        if (threadHandles[i].joinable())
            threadHandles[i].join();
            cout << "Thread-" << i << " NOT joinable!\n";
and the thread functions ...
1
2
3
4
5
6
7
8
9
10
void Task_PrintHelloWithIdAndExit(int id)
    cout << "Hello from thread id=" << id << "\n";
void Task_Safe_PrintHelloWithIdAndExit(int id, std::mutex &coutMutex)
    std::lock_guard<std::mutex> coutMutexGuard(coutMutex); // Upon creation, lock the mutex
    cout << "Hello from thread id=" << id << "\n";
and the compiler errors (it goes on ... to long to post)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
1>ex_Threads.cpp
1>ManageThreads.cpp
1>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include\memory(3597,35): error C2661: 'std::tuple<void (__cdecl *)(int,std::mutex &),int,std::mutex>::tuple': no overloaded function takes 3 arguments
1>(compiling source file 'ManageThreads.cpp')
1>    C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include\tuple(516,5):
1>    could be 'std::tuple<void (__cdecl *)(int,std::mutex &),int,std::mutex>::tuple(std::allocator_arg_t,const _Alloc &,std::pair<_First,_Second> &&)'
1>        C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include\memory(3597,35):
1>        'std::tuple<void (__cdecl *)(int,std::mutex &),int,std::mutex>::tuple(std::allocator_arg_t,const _Alloc &,std::pair<_First,_Second> &&)': could not deduce template argument for 'std::pair<_First,_Second> &&' from 'std::mutex'
1>    C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include\tuple(510,5):
1>    or       'std::tuple<void (__cdecl *)(int,std::mutex &),int,std::mutex>::tuple(std::allocator_arg_t,const _Alloc &,const std::pair<_First,_Second> &)'
1>        C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include\memory(3597,35):
1>        'std::tuple<void (__cdecl *)(int,std::mutex &),int,std::mutex>::tuple(std::allocator_arg_t,const _Alloc &,const std::pair<_First,_Second> &)': could not deduce template argument for 'const std::pair<_First,_Second> &' from 'std::mutex'
1>    C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include\tuple(488,5):
1>    or       'std::tuple<void (__cdecl *)(int,std::mutex &),int,std::mutex>::tuple(std::allocator_arg_t,const _Alloc &,std::tuple<_Types2...> &&)'
1>        C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include\memory(3597,35):
1>        'std::tuple<void (__cdecl *)(int,std::mutex &),int,std::mutex>::tuple(std::allocator_arg_t,const _Alloc &,std::tuple<_Types2...> &&)': could not deduce template argument for 'std::tuple<_Types2...> &&' from 'std::mutex'
1>    C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include\tuple(480,5):
1>    or       'std::tuple<void (__cdecl *)(int,std::mutex &),int,std::mutex>::tuple(std::allocator_arg_t,const _Alloc &,const std::tuple<_Types2...> &)'
1>        C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include\memory(3597,35):
1>        'std::tuple<void (__cdecl *)(int,std::mutex &),int,std::mutex>::tuple(std::allocator_arg_t,const _Alloc &,const std::tuple<_Types2...> &)': could not deduce template argument for 'const std::tuple<_Types2...> &' from 'std::mutex'
1>    C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include\tuple(463,5):
1>    or       'std::tuple<void (__cdecl *)(int,std::mutex &),int,std::mutex>::tuple(std::allocator_arg_t,const _Alloc &,std::tuple<void (__cdecl *)(int,std::mutex &),int,std::mutex> &&)'
1>        C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include\memory(3597,35):
1>        'std::tuple<void (__cdecl *)(int,std::mutex &),int,std::mutex>::tuple(std::allocator_arg_t,const _Alloc &,std::tuple<void (__cdecl *)(int,std::mutex &),int,std::mutex> &&)': could not deduce template argument for '__formal'
1>            C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include\tuple(462,51):
1>            'std::enable_if_t<false,int>' : Failed to specialize alias template
Last edited on I think the problem is that std::thread will copy the arguments by default but std::mutex is not copyable.
Try this instead:
 
thread t(Task_Safe_PrintHelloWithIdAndExit, i, std::ref(coutMutex));
Last edited on Thanks Peter. That was it.
1. std::ref(coutMutex) was necessary, for the reason you stated.
2. "t" needs to be std::moved() into the pair, since it can't be copied.
3. Either std::map::emplace and std::map::insert() will work.
Simpler working example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <iostream>
#include <sstream>
#include <map>
#include <mutex>
#include <thread>
using std::cout;
using std::cin; 
using std::endl;
using std::ostringstream;
using std::map;
using std::pair;
using std::mutex; 
using std::lock_guard;
using std::thread;
void Task_PrintHelloAndSleep(int i, std::mutex &mu)
    lock_guard<mutex> coutGuard(mu); 
    cout << "[Thread-" << i << "] " << "Hello!\n";
struct RunMe
    int nThreads = -1; 
    map<int, thread> tmap; 
    RunMe(int nThreads) : nThreads(nThreads)
    // Notice the order in which threads cout isn't guaranteed - any thread can grab the mutex before other threads, even though the threads were created in order!
    void SafeCout(void)
        for (int i = 0; i < nThreads; i++)
            thread t(Task_PrintHelloAndSleep, i, std::ref(coutMtx));
            tmap.emplace(std::make_pair(i, std::move(t)));
        for (int i = 0; i < nThreads; i++)
            if (tmap[i].joinable())
                tmap[i].join();
        tmap.clear(); // Cleanup so that SafeCout can be called again with new threads, same index.
    mutex coutMtx; 
int main()
    RunMe rme(10); 
    const int nRuns = 100; // Run it a bunch of times to check that successes weren't a fluke
    for (int i = 0; i < nRuns; i++) { 
        cout << "[Run #" << i << "]\n";
        rme.SafeCout(); 
    cout << "End of program.\n";