I wrote an test project for learning the c++ thread, but some error happened in my program.
the code is sample that a class provide a function that can add data to container and the data will be print in thread, and the data which has been printed will be removed from container.
that is the code:
#include <mutex>
#include <thread>
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
class Manager
{
public:
Manager()
{
const auto expression = [&]()->void {return threadProc(); };
thread(expression).detach();
}
~Manager() {}
void addData(int num)
{
if (m_data.lock())
m_data.data.push_back(num);
}
private:
struct
{
vector<int> data;
unique_lock<mutex> lock()
{
return unique_lock<mutex>(m);
}
private:
mutex m;
}m_data;
void threadProc()
{
while (true)
{
if (m_data.lock())
{
for_each(m_data.data.begin(), m_data.data.end(), [](int num)
{
cout << num << endl;
});
m_data.data.clear();
}
}
}
};
int main()
{
Manager manager;
manager.addData(1);
system("pause");
}
when it runs, it will shows
error info
Could you please tell me where is the problem? thanks!
The temporary unique_lock returned by lock() is destroyed right away, unlocking the mutex. Access to data is not in fact protected from concurrent access. Your program exhibits undefined behavior by way of a data race.
Related
I have one main thread that will send an async job to the task queue on the other thread. And this main thread can trigger a destroy action at any time, which could cause the program to crash in the async task, a piece of very much simplified code like this:
class Bomb {
public:
int trigger;
mutex my_mutex;
};
void f1(Bomb *b) {
lock_guard<std::mutex> lock(b->my_mutex); //won't work! Maybe b have been destructed!
sleep(1);
cout<<"wake up.."<<b->trigger<<"..."<<endl;
}
int main()
{
Bomb *b = new Bomb();
b->trigger = 1;
thread t1(f1, b);
sleep(1);
//lock here won't work
delete b;//in actual case it is triggered by outside users
t1.join();
return 0;
}
The lock in f1 won't work since the destructor can be called first and trying to read mutex will crash. Put lock in destructor or before the delete also won't work for the same reason.
So is there any better way in this situation? Do I have to put mutex in the global scope and inside destructor to solve the issue?
In code, my comment looks like this :
#include <future>
#include <mutex>
#include <iostream>
#include <chrono>
#include <thread>
// do not use : using namespace std;
class Bomb
{
public:
void f1()
{
m_future = std::async(std::launch::async,[this]
{
async_f1();
});
}
private:
void async_f1()
{
using namespace std::chrono_literals;
std::lock_guard<std::mutex> lock{ m_mtx };
std::cout << "wake up..\n";
std::this_thread::sleep_for(1s);
std::cout << "thread done.\n";
}
std::future<void> m_future;
std::mutex m_mtx;
};
int main()
{
{
std::cout << "Creating bomb\n";
Bomb b; // no need to use unecessary new
b.f1();
}
std::cout << "Bomb destructed\n";
return 0;
}
I am currently practicing the use of multiple threads in C++. The program is simplified as follow. In this case, I have a global variable Obj, and within each task, a get function is processed by thread and thread detach will be called after.
In practice, get may take a great amount of time to run. If there are many tasks, get will be called repetitively (since each task has its own get function). I wonder if I can design a program where when one task has already obtained the data using get function and the data has been wrote to obj.text, then the rest of tasks can directly access or wait for the data from obj.text.
Can I use std::shared_ptr, std::future, std::async in c++ to implement this? If so, how to design the program? Any advice is greatly appreciated.
#include <chrono>
#include <future>
#include <iostream>
#include <memory>
#include <thread>
#include <vector>
using namespace std;
class Info {
public:
Info() { Ids = 10; };
int Ids;
std::string text;
};
Info Objs;
class Module {
public:
Module() {}
virtual void check(int &id){};
virtual void get(){};
};
class task1 : public Module {
public:
task1() { std::cout << "task1" << std::endl; }
void check(int &id) override {
thread s(&task1::get, this);
s.detach();
};
// The function will first do some other work (here, I use sleep to represent
// that) then set the value of Objs.text
void get() override {
// The task may take 2 seconds , So use text instead
std::this_thread::sleep_for(std::chrono::seconds(5));
Objs.text = "AAAA";
std::cout << Objs.text << std::endl;
};
};
class task2 : public Module {
public:
task2() { std::cout << "task2" << std::endl; }
void check(int &id) override {
thread s(&task2::get, this);
s.detach();
};
// The function will first do some other work (here, I use sleep to represent
// that) then set the value of Objs.text
void get() {
std::this_thread::sleep_for(std::chrono::seconds(5));
Objs.text = "AAAA";
std::cout << Objs.text << std::endl;
};
};
int main() {
std::vector<std::unique_ptr<Module>> modules;
modules.push_back(std::make_unique<task1>());
modules.push_back(std::make_unique<task2>());
for (auto &m : modules) {
m->check(Objs.Ids);
}
std::this_thread::sleep_for(std::chrono::seconds(12));
return 0;
}
It is a plain producer-consumer problem.
You have multiple “get()” producers. And did not implemented consumers yet.
First, you should have multiple “Info” for multithread. If there is only one Info, multithread programming is useless. I recommend “concurrent_queue”.
Second, “detach()” is not a good idea. You can’t manage child threads. You’d better use “join()”
My code sample follows. I used Visual Studio 2022
#include <chrono>
#include <iostream>
#include <thread>
#include <vector>
#include <concurrent_queue.h>
using namespace std;
class Info {
public:
Info() { Ids = 10; };
int Ids;
std::string text;
};
concurrency::concurrent_queue<Info> Objs;
void producer()
{
while (true) {
Info obj;
std::this_thread::sleep_for(std::chrono::seconds(5));
obj.text = "AAAA\n";
Objs.push(obj);
}
}
void consumer()
{
while (true) {
std::this_thread::sleep_for(std::chrono::seconds(1));
Info obj;
bool got_it = Objs.try_pop(obj);
if (got_it) {
std::cout << obj.text;
}
}
}
int main() {
const int NUM_CORES = 6;
std::vector<std::thread> threads;
for (int i = 0; i < NUM_CORES / 2; ++i)
threads.emplace_back(producer);
for (int i = 0; i < NUM_CORES / 2; ++i)
threads.emplace_back(consumer);
for (auto& th : threads) th.join();
}
I am calling a member function inside the thread. I have a member variable that I edit inside this function for which I have applied the lock.
Is this logic okay or do even the reads need to be thread-safe?
The code is something like the one shown below:
#include <iostream>
#include <vector>
#include <mutex>
#include <future>
#include <memory>
using namespace std;
class A
{
int occurrence;
mutex m;
public:
A() = default;
void operation()
{
/*--
Some operations where I only read the class members other than occurrence but do not modify them.
--*/
{
lock_guard<mutex> my_lock(m);
occurence++;
}
}
int getOccurence()
{
return occurence;
}
};
class B
{
shared_ptr<A> a{};
public:
B(shared_ptr<A>& a_ptr)
{
a = a_ptr;
}
void callAoperation()
{
a->operation();
}
};
int main()
{
shared_ptr<A> a = make_shared<A>();
B* b = new B(a);
vector<std::future<void>> async_call;
int i = 0;
while (i < 10)
{
async_call.push_back(async(launch::async, &B::callAoperation, b));
i++;
}
for (auto&& fut : async_call)
{
fut.wait();
}
cout << a->getOccurence();
}
Inside my operation method, I modify only one variable, others I just read.
The class variable which I am modifying is inside a lock.
I want to know, Is this logic correct or will it have some issues?
The logic is not correct. While reading a variable, another thread can modify it, leading the data race. You should use std::shared_mutex and protect reading from writing using std::shared_lock, prevent writing while reading with std::unique_lock.
Could you please review and suggest what is wrong with this code?
It either crashes on line 21 (cond_var_.wait(lock); in the gc_thread_proc()) or locks on line 56 (lock.lock(); in release()).
#include <condition_variable>
#include <deque>
#include <functional>
#include <mutex>
#include <thread>
#include <vector>
#include <iostream>
class stream {
std::deque<int> pending_cleanups_;
std::mutex mut_{};
bool continue_{true};
std::thread gc_worker_;
std::condition_variable cond_var_;
void gc_thread_proc() {
while (true) {
std::vector<int> events_to_clean;
std::unique_lock<std::mutex> lock(mut_);
while (pending_cleanups_.empty() && continue_) {
cond_var_.wait(lock);
}
if (!continue_) {
break;
}
std::move(std::begin(pending_cleanups_), std::end(pending_cleanups_), std::back_inserter(events_to_clean));
pending_cleanups_.clear();
}
}
public:
explicit stream() : gc_worker_(&stream::gc_thread_proc, this) {}
void register_pending_event(int val) {
{
std::lock_guard<std::mutex> lock_guard(mut_);
pending_cleanups_.push_back(val);
}
cond_var_.notify_one();
}
void release() {
std::unique_lock<std::mutex> lock(mut_);
if (!continue_) {
return;
}
continue_ = false;
lock.unlock();
cond_var_.notify_one();
gc_worker_.join();
lock.lock();
pending_cleanups_.clear();
}
~stream() { release(); }
};
int main() {
int N=100000;
while(N--) {
std::cout << ".";
stream s;
}
std::cout << "ok";
return 0;
}
Changing order of members makes this problem go away - when cond_var_ is put before the gc_worker_ problem doesn't reproduce. But I guess it doesn't fix it just hides it somehow...
non-static data members are initialized in order of declaration in the class definition: https://en.cppreference.com/w/cpp/language/initializer_list
3) Then, non-static data members are initialized in order of declaration in the class definition.
In your case, since your std::thread member is initialized to start executing in its constructor, cv may not be initialized when it's used in gc_thread_proc. A command way to have a std::thread member is to move assign it in the class contructor, i.e.
class stream {
std::thread gc_worker_;
std::condition_variable cond_var_;
public:
stream(): {
gc_work = std::move(std::thread(&stream::gc_thread_proc, this));
}
};
I wrote this sample program to mimic what I'm trying to do in a larger program.
I have some data that will come from the user and be passed into a thread for some processing. I am using mutexes around the data the flags to signal when there is data.
Using the lambda expression, is a pointer to *this send to the thread? I seem to be getting the behavior I expect in the cout statement.
Are the mutexes used properly around the data?
Is putting the atomics and mutexes as a private member of the class a good move?
foo.h
#pragma once
#include <atomic>
#include <thread>
#include <vector>
#include <mutex>
class Foo
{
public:
Foo();
~Foo();
void StartThread();
void StopThread();
void SendData();
private:
std::atomic<bool> dataFlag;
std::atomic<bool> runBar;
void bar();
std::thread t1;
std::vector<int> data;
std::mutex mx;
};
foo.c
#include "FooClass.h"
#include <thread>
#include <string>
#include <iostream>
Foo::Foo()
{
dataFlag = false;
}
Foo::~Foo()
{
StopThread();
}
void Foo::StartThread()
{
runBar = true;
t1 = std::thread([=] {bar(); });
return;
}
void Foo::StopThread()
{
runBar = false;
if(t1.joinable())
t1.join();
return;
}
void Foo::SendData()
{
mx.lock();
for (int i = 0; i < 5; ++i) {
data.push_back(i);
}
mx.unlock();
dataFlag = true;
}
void Foo::bar()
{
while (runBar)
{
if(dataFlag)
{
mx.lock();
for(auto it = data.begin(); it < data.end(); ++it)
{
std::cout << *it << '\n';
}
mx.unlock();
dataFlag = false;
}
}
}
main.cpp
#include "FooClass.h"
#include <iostream>
#include <string>
int main()
{
Foo foo1;
std::cout << "Type anything to end thread" << std::endl;
foo1.StartThread();
foo1.SendData();
// type something to end threads
char a;
std::cin >> a;
foo1.StopThread();
return 0;
}
You ensure that the thread is joined using RAII techniques? Check.
All data access/modification is either protected through atomics or mutexs? Check.
Mutex locking uses std::lock_guard? Nope. Using std::lock_guard wraps your lock() and unlock() calls with RAII. This ensures that even if an exception occurs while within the lock, that the lock is released.
Is putting the atomics and mutexes as a private member of the class a good move?
Its neither good nor bad, but in this scenario, where Foo is a wrapper for a std::thread that does work and controls the synchronization, it makes sense.
Using the lambda expression, is a pointer to *this send to the thread?
Yes, you can also do t1 = std::thread([this]{bar();}); to make it more explicit.
As it stands, with your dataFlag assignments after the locks, you may encounter problems. If you call SendData twice such that bar processes the first one but is halted before setting dataFlag = false so that the second call adds the data, sets the flag to true only to have bar set it back to false. Then, you'll have data that has been "sent" but bar doesn't think there's anything to process.
There may be other tricky situations, but this was just one example; moving it into the lock clears up that problem.
for example, your SendData should look like:
void Foo::SendData()
{
std::lock_guard<std::mutex> guard(mx);
for (int i = 0; i < 5; ++i) {
data.push_back(i);
}
dataFlag = true;
}