I have a class called "Vector", which by default holds 10.000 elements, which at all times must have the same value. This class is tested and works. Therefore I use the method setAndTest() from the class to set the value of all elements, which then immediately checks whether the Vector object is consistent (that all vector elements hold the same value).
In a new file "main.cpp", i have created two functions: writer() and main().
writer() creates a user-defined number of writer threads (between 1 & 100), each with their own unique id. Each writer sets and tests the shared Vector object to its id every second. If a writer detects an inconsistensy in a shared Vector object, setAndTest() returns false and the following error message should be printed: Error with thread #id
However, in 99% of the cases it prints Success with thread #id, whereas I expected that there would be more variation between the two.
Headers included in main.cpp file:
#include <iostream>
#include "Vector.hpp"
#include <pthread.h>
#include <unistd.h>
using namespace std;
Vector object and writer() function:
Vector VecObj; //The Vector object (Defined in global scope)
void* writer(void *threadid)
{
int threadid_ = *(int *)(threadid);
if(VecObj.setAndTest(threadid_))
{
std::cout << "\nSuccess with thread " << threadid_ << endl;
}else
{
std::cout << "\nError with thread " << threadid_ << endl;
}
return NULL;
}
main function:
int main()
{
start:
int numOfThreads = 1;
std::cout << "Enter amount of threads (must be between 1 & 100): ";
std::cin >> numOfThreads;
if(0 < numOfThreads && numOfThreads <= 100){
std::cout << "You entered " << numOfThreads << " threads" << endl;
}else{
std::cout << "Amount of threads must be between 1 & 100" << endl;
goto start;
}
pthread_t threadcreator[numOfThreads];
for(int i = 0; i < numOfThreads; i++){
pthread_create(&threadcreator[i], NULL, writer, &i);
sleep(1);
}
for(int i = 0; i < numOfThreads; i++){
pthread_join(threadcreator[i], NULL);
}
}
Vector Class (Vector.hpp):
#ifndef VECTOR_HPP_
#define VECTOR_HPP_
#include <pthread.h>
using namespace std;
//=======================================================
// Class: Vector
// contains a size_-size vector of integers.
// Use the function setAndTest to set all elements
// of the vector to a certain value and then test that
// the value is indeed correctly set
//=======================================================
class Vector
{
public:
Vector(unsigned int size = 10000) : size_(size)
{
vector_ = new int[size_];
set(0);
}
~Vector()
{
delete[] vector_;
}
bool setAndTest(int n)
{
set(n);
return test(n);
}
private:
void set(int n)
{
for(unsigned int i=0; i<size_; i++) vector_[i] = n;
}
bool test(int n)
{
for(unsigned int i=0; i<size_; i++) if(vector_[i] != n) return false;
return true;
}
int* vector_;
unsigned int size_;
};
#endif
You are passing each thread a pointer to the same int variable. That variable changes value on each loop iteration. writer() is expecting to receive the same int value that was given to pthread_create(), but that is not guaranteed in your code, even with the sleep() call.
To pass the int correctly, pass the actual int value itself rather than a pointer to the int, eg:
#include <iostream>
#include <vector>
#include <cstdint>
#include <pthread.h>
#include "Vector.hpp"
Vector VecObj;
void* writer(void *arg)
{
int threadid_ = static_cast<int>(reinterpret_cast<intptr_t>(arg));
if (VecObj.setAndTest(threadid_))
{
std::cout << "\nSuccess with thread " << threadid_ << std::endl;
}
else
{
std::cout << "\nError with thread " << threadid_ << std::endl;
}
return NULL;
}
int main()
{
int numOfThreads = 0;
do {
std::cout << "Enter amount of threads (must be between 1 & 100): ";
std::cin >> numOfThreads;
if (0 < numOfThreads && numOfThreads <= 100){
std::cout << "You entered " << numOfThreads << " threads" << std::endl;
break;
}
std::cout << "Amount of threads must be between 1 & 100" << std::endl;
}
while (true);
std::vector<pthread_t> threadcreator(numOfThreads);
for(int i = 0; i < numOfThreads; i++){
pthread_create(&threadcreator[i], NULL, writer, reinterpret_cast<void*>(i));
}
for(int i = 0; i < numOfThreads; i++){
pthread_join(threadcreator[i], NULL);
}
return 0;
}
If you really want to use int* pointers, then you will have to allocate a separate int for each thread, eg:
#include <iostream>
#include <vector>
#include <pthread.h>
#include "Vector.hpp"
Vector VecObj;
void* writer(void *arg)
{
int threadid_ = *static_cast<int*>(arg);
if (VecObj.setAndTest(threadid_))
{
std::cout << "\nSuccess with thread " << threadid_ << std::endl;
}
else
{
std::cout << "\nError with thread " << threadid_ << std::endl;
}
return NULL;
}
int main()
{
int numOfThreads = 0;
do {
std::cout << "Enter amount of threads (must be between 1 & 100): ";
std::cin >> numOfThreads;
if (0 < numOfThreads && numOfThreads <= 100){
std::cout << "You entered " << numOfThreads << " threads" << std::endl;
break;
}
std::cout << "Amount of threads must be between 1 & 100" << std::endl;
}
while (true);
std::vector<pthread_t> threadcreator(numOfThreads);
std::vector<int> threadids(numOfThreads);
for(int i = 0; i < numOfThreads; i++){
threadids[i] = i;
pthread_create(&threadcreator[i], NULL, writer, &threadids[i]);
}
for(int i = 0; i < numOfThreads; i++){
pthread_join(threadcreator[i], NULL);
}
return 0;
}
Or, if you really want to pass an int* pointer to a single int, use a std::conditional_variable or other waitable signal to make sure that each thread has actually captured the int value before allowing the loop to change its value, eg:
#include <iostream>
#include <vector>
#include <conditional_variable>
#include <mutex>
#include "Vector.hpp"
#include <pthread.h>
Vector VecObj;
std::condition_variable cv;
std::mutex cv_m;
bool captured = false;
void* writer(void *arg)
{
int threadid_;
{
std::lock_guard<std::mutex> lk(cv_m);
threadid_ = *static_cast<int*>(arg);
captured = true;
}
cv.notify_one();
if (VecObj.setAndTest(threadid_))
{
std::cout << "\nSuccess with thread " << threadid_ << std::endl;
}
else
{
std::cout << "\nError with thread " << threadid_ << std::endl;
}
return NULL;
}
int main()
{
int numOfThreads = 0;
do {
std::cout << "Enter amount of threads (must be between 1 & 100): ";
std::cin >> numOfThreads;
if (0 < numOfThreads && numOfThreads <= 100){
std::cout << "You entered " << numOfThreads << " threads" << std::endl;
break;
}
std::cout << "Amount of threads must be between 1 & 100" << std::endl;
}
while (true);
std::vector<pthread_t> threadcreator(numOfThreads);
for(int i = 0; i < numOfThreads; i++){
std::unique_lock<std::mutex> lk(cv_m);
captured = false;
pthread_create(&threadcreator[i], NULL, writer, &i);
cv.wait(lk, [](){ return captured; });
}
for(int i = 0; i < numOfThreads; i++){
pthread_join(threadcreator[i], NULL);
}
return 0;
}
UPDATE: oh, now I see another major problem. You have multiple threads writing to, and reading from, a single Vector object in memory without synchronization. That is not safe to do. While one thread is reading from an element in the Vector's array, another thread can be writing a new value to that same element, and there is no guarantee that the element will remain consistent across both operations. You MUST synchronize access to the Vector object since it is being shared across multiple threads, eg:
...
#include <mutex>
...
Vector VecObj;
std::mutex vec_m;
...
void* writer(void *threadid)
{
int threadid_ = ...;
bool testResult;
{
std::lock_guard lk(vec_m);
testResult = VecObj.setAndTest(threadid_);
}
if (testResult)
{
std::cout << "\nSuccess with thread " << threadid_ << std::endl;
}
else
{
std::cout << "\nError with thread " << threadid_ << std::endl;
}
return NULL;
}
...
Related
I have some code which I'm working on where a detached thread is spawned, does some work, and then should wait for a signal from main() before sending another signal back to main indicating that the thread has quit.
I'm fairly new to condition variables, however I have worked with some multi thread code before. (Mostly mutexes.)
This is what I tried to implement, but it doesn't behave the way I would have expected. (Likely I misunderstood something.)
The idea behind this is to pass a struct containing two flags to each detached thread. The first flag indicates that main() says "it is ok to exit, and drop off the end of the thread function". The second flag is set by the thread itself and signals to main() that the thread has indeed exited. (It's just to confirm the signal from main() is recieved ok and to send something back.)
#include <cstdlib> // std::atoi
#include <iostream>
#include <thread>
#include <vector>
#include <random>
#include <future>
#include <condition_variable>
#include <mutex>
struct ThreadStruct
{
int id;
std::condition_variable cv;
std::mutex m;
int ok_to_exit;
int exit_confirm;
};
void Pause()
{
std::cout << "Press enter to continue" << std::endl;
std::cin.get();
}
void detachedThread(ThreadStruct* threadData)
{
std::cout << "START: Detached Thread " << threadData->id << std::endl;
// Performs some arbitrary amount of work.
for(int i = 0; i < 100000; ++ i);
std::cout << "FINISH: Detached thread " << threadData->id << std::endl;
std::unique_lock<std::mutex> lock(threadData->m);
std::cout << "WAIT: Detached thread " << threadData->id << std::endl;
threadData->cv.wait(lock, [threadData]{return threadData->ok_to_exit == 1;});
std::cout << "EXIT: Detached thread " << threadData->id << std::endl;
threadData->exit_confirm = 1;
}
int main(int argc, char** argv)
{
int totalThreadCount = 1;
ThreadStruct* perThreadData = new ThreadStruct[totalThreadCount];
std::cout << "Main thread starting " << totalThreadCount << " thread(s)" << std::endl;
for(int i = totalThreadCount - 1; i >= 0; --i)
{
perThreadData[i].id = i;
perThreadData[i].ok_to_exit = 0;
perThreadData[i].exit_confirm = 0;
std::thread t(detachedThread, &perThreadData[i]);
t.detach();
}
for(int i{0}; i < totalThreadCount; ++i)
{
ThreadStruct *threadData = &perThreadData[i];
std::cout << "Waiting for lock - main() thread" << std::endl;
std::unique_lock<std::mutex> lock(perThreadData[i].m);
std::cout << "Lock obtained - main() thread" << std::endl;
perThreadData[i].cv.wait(lock);
threadData->ok_to_exit = 1;
// added after comment from Sergey
threadData->cv.notify_all();
std::cout << "Done - main() thread" << std::endl;
}
for(int i{0}; i < totalThreadCount; ++i)
{
std::size_t thread_index = i;
ThreadStruct& threadData = perThreadData[thread_index];
std::unique_lock<std::mutex> lock(threadData.m);
std::cout << "i=" << i << std::endl;
int &exit_confirm = threadData.exit_confirm;
threadData.cv.wait(lock, [exit_confirm]{return exit_confirm == 1;});
std::cout << "i=" << i << " finished!" << std::endl;
}
Pause();
return 0;
}
This runs to the line:
WAIT: Detached thread 0
but the detached thread never quits. What have I done wrong?
Edit: Further experimentation - is this helpful?
I thought it might be helpful to simplify things by removing a step. In the example below, main() does not signal to the detached thread, it just waits for a signal from the detached thread.
But again, this code hangs - after printing DROP... This means the detached thread exits ok, but main() doesn't know about it.
#include <cstdlib> // std::atoi
#include <iostream>
#include <thread>
#include <vector>
#include <random>
#include <future>
#include <condition_variable>
#include <mutex>
struct ThreadStruct
{
int id;
std::condition_variable cv;
std::mutex m;
int ok_to_exit;
int exit_confirm;
};
void Pause()
{
std::cout << "Press enter to continue" << std::endl;
std::cin.get();
}
void detachedThread(ThreadStruct* threadData)
{
std::cout << "START: Detached Thread " << threadData->id << std::endl;
// Performs some arbitrary amount of work.
for(int i = 0; i < 100000; ++ i);
std::cout << "FINISH: Detached thread " << threadData->id << std::endl;
std::unique_lock<std::mutex> lock(threadData->m);
std::cout << "EXIT: Detached thread " << threadData->id << std::endl;
threadData->exit_confirm = 1;
threadData->cv.notify_all();
std::cout << "DROP" << std::endl;
}
int main(int argc, char** argv)
{
int totalThreadCount = 1;
ThreadStruct* perThreadData = new ThreadStruct[totalThreadCount];
std::cout << "Main thread starting " << totalThreadCount << " thread(s)" << std::endl;
for(int i = totalThreadCount - 1; i >= 0; --i)
{
perThreadData[i].id = i;
perThreadData[i].ok_to_exit = 0;
perThreadData[i].exit_confirm = 0;
std::thread t(detachedThread, &perThreadData[i]);
t.detach();
}
for(int i{0}; i < totalThreadCount; ++i)
{
std::size_t thread_index = i;
ThreadStruct& threadData = perThreadData[thread_index];
std::cout << "Waiting for mutex" << std::endl;
std::unique_lock<std::mutex> lock(threadData.m);
std::cout << "i=" << i << std::endl;
int &exit_confirm = threadData.exit_confirm;
threadData.cv.wait(lock, [exit_confirm]{return exit_confirm == 1;});
std::cout << "i=" << i << " finished!" << std::endl;
}
Pause();
return 0;
}
Your lambda is capturing by-value so it will never see the changes made to exit_confim.
Capture by-reference instead:
int& exit_confirm = threadData.exit_confirm;
threadData.cv.wait(lock, [&exit_confirm] { return exit_confirm == 1; });
// ^
// | capture by-reference
You also need to delete[] what you new[] so do
delete[] ThreadStruct;
when you're done with the the structs.
I also noticed some heap usage after free but that magically went away when I made some simplifications to the code. I didn't investigate that further.
Some suggestions:
Move code into the ThreadStruct class that deals with ThreadStruct member variables and locks. It usually makes it simpler to read and maintain.
Remove unused variables and headers.
Don't use new[]/delete[]. For this example, you could use a std::vector<ThreadStruct> instead.
Don't detach() at all - I haven't done anything about that below, but I suggest using join() (on attached threads) to do the final synchronization. That's what it's there for.
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
struct ThreadStruct {
int id;
// move this function into the ThreadStruct class
void detachedThread() {
std::cout << "START: Detached Thread " << id << std::endl;
// Performs some arbitrary amount of work (optimized away here)
std::cout << "FINISH: Detached thread " << id << std::endl;
std::lock_guard<std::mutex> lock(m);
std::cout << "EXIT: Detached thread " << id << std::endl;
exit_confirm = 1;
cv.notify_all();
std::cout << "DROP" << std::endl;
}
// add support functions instead of doing these things in your normal code
void wait_for_exit_confirm() {
std::unique_lock<std::mutex> lock(m);
cv.wait(lock, [this] { return exit_confirm == 1; });
}
void spawn_detached() {
std::thread(&ThreadStruct::detachedThread, this).detach();
}
private:
std::condition_variable cv;
std::mutex m;
int exit_confirm = 0; // initialize
};
With the above, main becomes a little cleaner:
int main() {
int totalThreadCount = 1;
std::vector<ThreadStruct> perThreadData(totalThreadCount);
std::cout << "Main thread starting " << perThreadData.size() << " thread(s)\n";
int i = 0;
for(auto& threadData : perThreadData) {
threadData.id = i++;
threadData.spawn_detached();
}
for(auto& threadData : perThreadData) {
std::cout << "Waiting for mutex" << std::endl;
std::cout << "i=" << threadData.id << std::endl;
threadData.wait_for_exit_confirm();
std::cout << "i=" << threadData.id << " finished!" << std::endl;
}
std::cout << "Press enter to continue" << std::endl;
std::cin.get();
}
For future interest: fixed the origional MWE posted in the question. There was two issues
not capturing local variable in lambda by reference (see other answer)
1 too many wait() calls
#include <cstdlib> // std::atoi
#include <iostream>
#include <thread>
#include <vector>
#include <random>
#include <future>
#include <condition_variable>
#include <mutex>
struct ThreadStruct
{
int id;
std::condition_variable cv;
std::mutex m;
int ok_to_exit;
int exit_confirm;
};
void Pause()
{
std::cout << "Press enter to continue" << std::endl;
std::cin.get();
}
void detachedThread(ThreadStruct* threadData)
{
std::cout << "START: Detached Thread " << threadData->id << std::endl;
// Performs some arbitrary amount of work.
for (int i = 0; i < 100000; ++i);
std::cout << "FINISH: Detached thread " << threadData->id << std::endl;
std::unique_lock<std::mutex> lock(threadData->m);
std::cout << "WAIT: Detached thread " << threadData->id << std::endl;
threadData->cv.wait(lock, [&threadData]{return threadData->ok_to_exit == 1;});
std::cout << "EXIT: Detached thread " << threadData->id << std::endl;
threadData->exit_confirm = 1;
threadData->cv.notify_all();
std::cout << "DROP" << std::endl;
}
int main(int argc, char** argv)
{
int totalThreadCount = 1;
ThreadStruct* perThreadData = new ThreadStruct[totalThreadCount];
std::cout << "Main thread starting " << totalThreadCount << " thread(s)" << std::endl;
for (int i = totalThreadCount - 1; i >= 0; --i)
{
perThreadData[i].id = i;
perThreadData[i].ok_to_exit = 0;
perThreadData[i].exit_confirm = 0;
std::thread t(detachedThread, &perThreadData[i]);
t.detach();
}
for(int i{0}; i < totalThreadCount; ++ i)
{
ThreadStruct *threadData = &perThreadData[i];
std::cout << "Waiting for lock - main() thread" << std::endl;
std::unique_lock<std::mutex> lock(perThreadData[i].m);
std::cout << "Lock obtained - main() thread" << std::endl;
//perThreadData[i].cv.wait(lock, [&threadData]{return threadData->ok_to_exit == 1;});
std::cout << "Wait complete" << std::endl;
threadData->ok_to_exit = 1;
threadData->cv.notify_all();
std::cout << "Done - main() thread" << std::endl;
}
for (int i{ 0 }; i < totalThreadCount; ++i)
{
std::size_t thread_index = i;
ThreadStruct& threadData = perThreadData[thread_index];
std::cout << "Waiting for mutex" << std::endl;
std::unique_lock<std::mutex> lock(threadData.m);
std::cout << "i=" << i << std::endl;
int& exit_confirm = threadData.exit_confirm;
threadData.cv.wait(lock, [&exit_confirm] {return exit_confirm == 1; });
std::cout << "i=" << i << " finished!" << std::endl;
}
Pause();
return 0;
}
UPDATE: If i change async_receive_from to receive_from then there will not be any problems with rebinding. Somehow async... causes that. Previously i had one thread for every socket(with receive_from), but i had to make it work in one thread as too many threads are spawned during programm runs.
Socket is closed (i have checked), but rebinding to it causes an error. Here is an example:
#include "stdafx.h"
#include "Mmsystem.h"// for accurate timers
#pragma comment (lib,"Winmm.lib")// for accurate timers
using namespace std;
typedef shared_ptr<boost::asio::ip::udp::socket> SHP_Socket;
boost::asio::io_service io_service_;
vector<int> ports;
vector<SHP_Socket> vecSock;
vector<boost::asio::ip::udp::endpoint> vecEndpoint;
vector<boost::shared_ptr<boost::thread>> receive_threads;
bool process_all_finishing;
uint8_t Data[8000];
void receive_h(boost::system::error_code ec, size_t szPack, int i)
{
if (process_all_finishing == false)
{
cout << "\n" << i;
string f = boost::to_string(Data);
int sz = f.size();
if (sz > 12)
{
vector<int> a;
for (int i = 0; i < 100; ++i)
a.push_back(i);
a.clear();
}
}
}
void Run_io()
{
while (process_all_finishing == false)
{
io_service_.run_one();
}
cout << "\nRun_io finished";
}
void receive()
{
while (process_all_finishing == false)
{
this_thread::sleep_for(chrono::milliseconds(1));
for (unsigned i = 0; i < vecSock.size(); ++i)
{
vecSock[i]->async_receive_from(boost::asio::buffer(Data, 8000), vecEndpoint[i], boost::bind(receive_h,_1,_2,i));
}
}
cout << "\nreceive finished";
}
int main()
{
timeBeginPeriod(1);
setlocale(LC_ALL, "Russian");
try
{
ports.push_back(29005);
ports.push_back(29007);
ports.push_back(29009);
ports.push_back(29001);
vecSock.resize(3);
vecEndpoint.resize(3);
for (int i = 0; i < 3; ++i)
{
vecSock[i].reset(new boost::asio::ip::udp::socket(io_service_, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), ports[i])));
}
boost::shared_ptr<boost::thread> thread_re(new boost::thread(receive));
boost::shared_ptr<boost::thread> thread_io(new boost::thread(Run_io));
receive_threads.push_back(thread_re);
receive_threads.push_back(thread_io);
cout << "\nvecSock=3 created, giving it to work for 1 second:";
this_thread::sleep_for(chrono::seconds(1));
process_all_finishing = true;
cout << "\nSent flag to stop threads and wait for threads to finish for 1 second";
this_thread::sleep_for(chrono::seconds(1));
for (int i = 0; i < vecSock.size(); ++i)
{
cout << "\nSocket " << i << " opened =\t" << vecSock[i]->is_open();
vecSock[i]->cancel();
vecSock[i]->close();
cout << "\nSocket " << i << " counter =\t" << vecSock[i].use_count();
cout << "\nSocket " << i << " opened =\t" << vecSock[i]->is_open();
vecSock[i].reset();
cout << "\nSocket " << i << " counter =\t" << vecSock[i].use_count();
cout << "\n";
}
this_thread::sleep_for(chrono::seconds(1));
vecSock.clear();
vecSock.resize(4);
vecEndpoint.resize(4);
for (int i = 0; i < 4; ++i)
{
vecSock[i].reset(new boost::asio::ip::udp::socket(io_service_, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), ports[i])));
}
cout << "\nvecSock=4 created";
}
catch (boost::exception& e)
{
cerr <<"\n\a\a"<< boost::diagnostic_information(e);
system("pause");
}
return 0;
}
This causes an binding error (exeption) which i have redirected to console with try-catch.
Throw location unknown (consider using BOOST_THROW_EXCEPTION) Dynamic
exception type: class boost::exception_detail::clone_impl > std::exception::what: bind: Usually are
allowed only one usage of each socket address (Protocol/network
address/port)
Can ony one help? I have tried all, what I could find in references c++ and boost and nothing helped this error.
I saw this example as part of the TinyThread c++ library and noticed how it is incrementing gCount on a secondary thread, and then being printed by the main thread. See test 5
/* -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; -*-
Copyright (c) 2010-2012 Marcus Geelnard
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#include <iostream>
#include <list>
#include <tinythread.h>
#include <fast_mutex.h>
using namespace std;
using namespace tthread;
// HACK: Mac OS X and early MinGW do not support thread-local storage
#if defined(__APPLE__) || (defined(__MINGW32__) && (__GNUC__ < 4))
#define NO_TLS
#endif
// Thread local storage variable
#ifndef NO_TLS
thread_local int gLocalVar;
#endif
// Mutex + global count variable
mutex gMutex;
fast_mutex gFastMutex;
int gCount;
// Condition variable
condition_variable gCond;
// Thread function: Thread ID
void ThreadIDs(void * aArg)
{
cout << " My thread id is " << this_thread::get_id() << "." << endl;
}
#ifndef NO_TLS
// Thread function: Thread-local storage
void ThreadTLS(void * aArg)
{
gLocalVar = 2;
cout << " My gLocalVar is " << gLocalVar << "." << endl;
}
#endif
// Thread function: Mutex locking
void ThreadLock(void * aArg)
{
for(int i = 0; i < 10000; ++ i)
{
lock_guard<mutex> lock(gMutex);
++ gCount;
}
}
// Thread function: Mutex locking
void ThreadLock2(void * aArg)
{
for(int i = 0; i < 10000; ++ i)
{
lock_guard<fast_mutex> lock(gFastMutex);
++ gCount;
}
}
// Thread function: Condition notifier
void ThreadCondition1(void * aArg)
{
lock_guard<mutex> lock(gMutex);
-- gCount;
gCond.notify_all();
}
// Thread function: Condition waiter
void ThreadCondition2(void * aArg)
{
cout << " Wating..." << flush;
lock_guard<mutex> lock(gMutex);
while(gCount > 0)
{
cout << "." << flush;
gCond.wait(gMutex);
}
cout << "." << endl;
}
// Thread function: Yield
void ThreadYield(void * aArg)
{
// Yield...
this_thread::yield();
}
// Thread function: Detach
void ThreadDetach(void * aArg)
{
// We don't do anything much, just sleep a little...
this_thread::sleep_for(chrono::milliseconds(100));
}
// This is the main program (i.e. the main thread)
int main()
{
// Test 1: Show number of CPU cores in the system
cout << "PART I: Info" << endl;
cout << " Number of processor cores: " << thread::hardware_concurrency() << endl;
// Test 2: thread IDs
cout << endl << "PART II: Thread IDs" << endl;
{
// Show the main thread ID
cout << " Main thread id is " << this_thread::get_id() << "." << endl;
// Start a bunch of child threads - only run a single thread at a time
thread t1(ThreadIDs, 0);
t1.join();
thread t2(ThreadIDs, 0);
t2.join();
thread t3(ThreadIDs, 0);
t3.join();
thread t4(ThreadIDs, 0);
t4.join();
}
// Test 3: thread local storage
cout << endl << "PART III: Thread local storage" << endl;
#ifndef NO_TLS
{
// Clear the TLS variable (it should keep this value after all threads are
// finished).
gLocalVar = 1;
cout << " Main gLocalVar is " << gLocalVar << "." << endl;
// Start a child thread that modifies gLocalVar
thread t1(ThreadTLS, 0);
t1.join();
// Check if the TLS variable has changed
if(gLocalVar == 1)
cout << " Main gLocalID was not changed by the child thread - OK!" << endl;
else
cout << " Main gLocalID was changed by the child thread - FAIL!" << endl;
}
#else
cout << " TLS is not supported on this platform..." << endl;
#endif
// Test 4: mutex locking
cout << endl << "PART IV: Mutex locking (100 threads x 10000 iterations)" << endl;
{
// Clear the global counter.
gCount = 0;
// Start a bunch of child threads
list<thread *> threadList;
for(int i = 0; i < 100; ++ i)
threadList.push_back(new thread(ThreadLock, 0));
// Wait for the threads to finish
list<thread *>::iterator it;
for(it = threadList.begin(); it != threadList.end(); ++ it)
{
thread * t = *it;
t->join();
delete t;
}
// Check the global count
cout << " gCount = " << gCount << endl;
}
// Test 5: fast_mutex locking
cout << endl << "PART V: Fast mutex locking (100 threads x 10000 iterations)" << endl;
{
// Clear the global counter.
gCount = 0;
// Start a bunch of child threads
list<thread *> threadList;
for(int i = 0; i < 100; ++ i)
threadList.push_back(new thread(ThreadLock2, 0));
// Wait for the threads to finish
list<thread *>::iterator it;
for(it = threadList.begin(); it != threadList.end(); ++ it)
{
thread * t = *it;
t->join();
delete t;
}
// Check the global count
cout << " gCount = " << gCount << endl;
}
// Test 6: condition variable
cout << endl << "PART VI: Condition variable (40 + 1 threads)" << endl;
{
// Set the global counter to the number of threads to run.
gCount = 40;
// Start the waiting thread (it will wait for gCount to reach zero).
thread t1(ThreadCondition2, 0);
// Start a bunch of child threads (these will decrease gCount by 1 when they
// finish)
list<thread *> threadList;
for(int i = 0; i < 40; ++ i)
threadList.push_back(new thread(ThreadCondition1, 0));
// Wait for the waiting thread to finish
t1.join();
// Wait for the other threads to finish
list<thread *>::iterator it;
for(it = threadList.begin(); it != threadList.end(); ++ it)
{
thread * t = *it;
t->join();
delete t;
}
}
// Test 7: yield
cout << endl << "PART VII: Yield (40 + 1 threads)" << endl;
{
// Start a bunch of child threads
list<thread *> threadList;
for(int i = 0; i < 40; ++ i)
threadList.push_back(new thread(ThreadYield, 0));
// Yield...
this_thread::yield();
// Wait for the threads to finish
list<thread *>::iterator it;
for(it = threadList.begin(); it != threadList.end(); ++ it)
{
thread * t = *it;
t->join();
delete t;
}
}
// Test 8: sleep
cout << endl << "PART VIII: Sleep (10 x 100 ms)" << endl;
{
// Sleep...
cout << " Sleeping" << flush;
for(int i = 0; i < 10; ++ i)
{
this_thread::sleep_for(chrono::milliseconds(100));
cout << "." << flush;
}
cout << endl;
}
// Test 9: detach
cout << endl << "PART IX: Detach" << endl;
{
thread t(ThreadDetach, 0);
t.detach();
cout << " Detached from thread." << endl;
}
}
So I tried to make my own code, but not only does the main thread not see the variable increment, but when I added sleep to the worker thread it no longer tries to print the variable on the main thread. Because of these two problems the worker thread never quits either.
#include <iostream>
#include <list>
#include "tinythread.h"
#include "fast_mutex.h"
using namespace std;
using namespace tthread;
fast_mutex workerThreadMutex;
bool killThread = false;
int number = 0;
void workerThread(void * aArg)
{
bool running = true;
int number = 0;
while(running)
{
lock_guard<fast_mutex> lock(workerThreadMutex);
number++;
cout << "secondThread::" << number << endl;
this_thread::sleep_for(chrono::milliseconds(1000));
if(killThread)
running =true;
}
}
int main()
{
thread* wThread = new thread(workerThread, 0);
bool running = true;
while(running)
{
this_thread::sleep_for(chrono::milliseconds(100));
if(workerThreadMutex.try_lock())
{
cout << "mainThread::" << number << endl;
if(number == 100)
{
killThread = true;
running = false;
}
workerThreadMutex.unlock();
}
}
wThread->join();
delete wThread;
return 0;
}
Can you help?
edit: made a change to my code
edit: fixed local variable issue, it works now, except for the kill thread boolean is not being detected so the secondary thread (worker thread) is not exiting.
edit: fixed issue with local variable
edit: fixed issue with sleep
edit: fixed issue killThread
edit: all problems fixed
#include <iostream>
#include <list>
#include "tinythread.h"
#include "fast_mutex.h"
using namespace std;
using namespace tthread;
fast_mutex workerThreadMutex;
bool killThread = false;
int number = 0;
bool running = true;
void workerThread(void * aArg)
{
while(running)
{
{
lock_guard<fast_mutex> lock(workerThreadMutex);
number++;
if(killThread)
running =false;
}
this_thread::sleep_for(chrono::milliseconds(50));
}
}
int main()
{
thread* wThread = new thread(workerThread, 0);
while(running)
{
this_thread::sleep_for(chrono::milliseconds(100));
if(workerThreadMutex.try_lock())
{
cout << "mainThread::" << number << endl;
if(number > 100)
{
killThread = true;
//running = false;
}
workerThreadMutex.unlock();
}
}
wThread->join();
delete wThread;
return 0;
}
edit: further modification to show portable c++11 friendly inter thread communication
#include <iostream>
#include <sstream>
#include "tinythread.h"
#include "fast_mutex.h"
using namespace std;
using namespace tthread;
fast_mutex workerThreadMutex;
bool killThread = false;
int number = 0;
bool running = true;
string message = "";
void workerThread(void * aArg)
{
while(running)
{
{
lock_guard<fast_mutex> lock(workerThreadMutex);
if(message=="")
{
number++;
ostringstream Convert;
Convert << number;
message = Convert.str();
}
if(killThread)
running =false;
}
this_thread::sleep_for(chrono::milliseconds(1));
}
}
int main()
{
thread* wThread = new thread(workerThread, 0);
bool stopMainThreadLoop = false;
while(!stopMainThreadLoop)
{
this_thread::sleep_for(chrono::milliseconds(10));
if(workerThreadMutex.try_lock())
{
//cout << "mainThread::" << number << endl;
cout << "mainThread::" << message << endl;
//if(number > 100)
if(message == "100")
{
killThread = true;
stopMainThreadLoop = true;
}
message = "";
workerThreadMutex.unlock();
}
}
wThread->join();
delete wThread;
return 0;
}
Look at this part of your code
int number = 0; // Global variable, i.e. will be used by main
void workerThread(void * aArg)
{
bool running = true;
int number = 0; // Local variable, i.e. will be used by in the function
So mainis using one variable and the function (aka the thread) is using another. Consequently main can't see any increments.
Just remove the local variable.
For the sleep part
I think the problem is that you do the sleep while holding the mutex.
Try this:
void workerThread(void * aArg)
{
bool running = true;
//---------------------- int number = 0;
while(running)
{
{
lock_guard<fast_mutex> lock(workerThreadMutex);
number++;
//cout << "secondThread::" << number << endl;
if(killThread)
running =true;
}
this_thread::sleep_for(chrono::milliseconds(1000));
}
}
For the kill:
Try this
if(number >= 100) // Changed
{
killThread = true;
running = false;
}
Inside the function
if(killThread)
running =true; // Should be false
I'm playing with C++(11) STL and got the following problem.
The basic idea for this code is:
I have a "trigger" function, an "add" function and a flag(false by default). If the flag is false the "add" function's going to push the threadID to a queue, otherwise it's going to insert the threadID to the set. When the trigger function is called, it set the flag to "true" and move threadIDs from the queue to the set.
I initialized 100 threads and use one of the thread to run the trigger function(in the code it's thread NO.30). Ideally the result should have 0 elements in the queue and 99 elements in the set.
However, sometimes the result is correct, sometimes I missed some numbers in the set, and sometimes I got the EXC_BAD_ACCESS error.
Could anyone help? Thank you.
#include <iostream>
#include <thread>
#include <vector>
#include <unordered_set>
#include <queue>
#include <mutex>
#include <atomic>
using namespace std;
bool flag = false;
queue<int> q;
unordered_set<int> s;
mutex mu;
void trigger()
{
mu.lock();
flag = true;
mu.unlock();
while( !q.empty() ){
s.insert(q.front());
q.pop();
}
}
void add(int id)
{
mu.lock();
if( !flag )
q.push(id);
else {
if ( s.find(id) == s.end() ){
s.insert(id);
}
}
mu.unlock();
}
void missing()
{
cout << "Missing Numbers: ";
for (int i = 1; i <= 100; i++) {
if( s.find(i) == s.end() )
cout << i << " ";
}
cout << endl;
}
int main()
{
vector<thread> threads;
for (int i = 0; i < 100; i++){
if ( i == 29 ) threads.push_back(thread(trigger));
else threads.push_back(thread(add, i+1));
}
for (int i = 0; i < 100; i++){
threads[i].join();
}
cout << "Q size: " << q.size() << endl;
cout << "S size: " << s.size() << endl;
missing();
}
You have 1 thread executing the trigger function and many threads executing the add function. Furthermore, you take care to guard some of the shared state but not all of it. See my comments/questions in the below code snippets.
void trigger()
{
// Only the 'flag' is protected from concurrent acceess
mu.lock();
flag = true;
mu.unlock();
// Why isn't 'q' or 's' protected by a lock?
while( !q.empty() ){
s.insert(q.front());
q.pop();
}
}
void add(int id)
{
// In this function both 'q' and 's' are protected from concurrent access
mu.lock();
if( !flag )
q.push(id);
else {
if ( s.find(id) == s.end() ){
s.insert(id);
}
}
mu.unlock();
}
Possible Solution
In general, you should protect any state being accessed concurrently. I'd also recommend using a lock type (e.g., lock_guard) instead of locking and unlocking the mutex directly (research RAII for why this is encouraged).
#include <iostream>
#include <mutex>
#include <queue>
#include <thread>
#include <unordered_set>
#include <vector>
using namespace std;
bool flag = false;
queue<int> q;
unordered_set<int> s;
mutex mu;
void trigger()
{
lock_guard<mutex> lock(mu);
flag = true;
while (!q.empty())
{
s.insert(q.front());
q.pop();
}
}
void add(int id)
{
lock_guard<mutex> lock(mu);
if (!flag)
{
q.push(id);
}
else
{
if (s.find(id) == s.end())
{
s.insert(id);
}
}
}
void missing()
{
cout << "Missing Numbers: ";
for (int i = 1; i <= 100; ++i)
{
if (s.find(i) == s.end())
{
cout << i << " ";
}
}
cout << endl;
}
int main()
{
vector<thread> threads;
for (int i = 0; i < 100; ++i)
{
if (i == 29)
{
threads.push_back(thread(trigger));
}
else
{
threads.push_back(thread(add, i + 1));
}
}
for (int i = 0; i < 100; ++i)
{
threads[i].join();
}
cout << "Q size: " << q.size() << endl;
cout << "S size: " << s.size() << endl;
missing();
return 0;
}
What's meaning of field uc_stack in struct ucontext_t?Who use it? The coroutine or the coroutine's signal handler? How can I test it? For example
#include <iostream>
#include <ucontext.h>
#include <queue>
#include <signal.h>
using namespace std;
void sigHandler(int signo)
{
printf("sigHandler:%x\n", &signo);
exit(-1);
}
queue<int> qProduct;
void consumer(ucontext_t* pConsumer, ucontext_t* pProducer)
{
char a[SIGSTKSZ] = {0};
while(1)
{
if(qProduct.size() > 0)
{
cout << __FUNCTION__ << "|" << qProduct.front() << endl;
qProduct.pop();
}
else
{
cout << pConsumer << "|" << pProducer << endl;
swapcontext(pConsumer, pProducer);
}
}
}
void producer(ucontext_t* pConsumer, ucontext_t* pProducer, bool* pFinished)
{
for(int i=0; i<10; i++)
{
if(qProduct.size() < 5)
{
qProduct.push(i);
cout << __FUNCTION__ << "|" << i << endl;
}
else
{
cout << pConsumer << "|P|" << pProducer << endl;
swapcontext(pProducer, pConsumer);
}
}
cout << pConsumer << "|P|" << pProducer << endl;
swapcontext(pProducer, pConsumer);
*pFinished = true;
}
int main(int argc, char* argv[])
{
ucontext_t Main, Consumer, Producer;
/* The stack for the iterator function. */
char consumer_stack[SIGSTKSZ];
char producer_stack[SIGSTKSZ];
cout << "SIGSTKSZ:" << SIGSTKSZ << endl;
/* Flag indicating that the iterator has completed. */
volatile bool bFinished = false;
getcontext(&Consumer);
Consumer.uc_link = &Main;
Consumer.uc_stack.ss_sp = consumer_stack;
Consumer.uc_stack.ss_size = sizeof(consumer_stack);
makecontext(&Consumer, (void (*)(void))consumer, 2, &Consumer, &Producer);
getcontext(&Producer);
Producer.uc_link = &Main;
Producer.uc_stack.ss_sp = producer_stack;
Producer.uc_stack.ss_size = sizeof(producer_stack);
makecontext(&Producer, (void (*)(void))producer, 3, &Consumer, &Producer, &bFinished);
if(!bFinished)
{
swapcontext(&Main, &Producer);
}
return 0;
}
Who use the stack "consumer_stack", "consumer" or "sigHandler"?How to prove it?