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.
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>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:
#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);
constint 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";