C++ 通过同步延长线程的生命周期(C+;+;11)
我有一个程序,它有一个以指针为arg的函数和一个main。main正在创建n线程,每个线程根据传递的C++ 通过同步延长线程的生命周期(C+;+;11),c++,multithreading,c++11,thread-safety,C++,Multithreading,C++11,Thread Safety,我有一个程序,它有一个以指针为arg的函数和一个main。main正在创建n线程,每个线程根据传递的arg在不同的内存区域上运行函数。然后连接线程,main在区域之间执行一些数据混合,并创建n新线程,这些线程执行与旧线程相同的操作 为了改进程序,我想让线程保持活动状态,消除创建线程所需的长时间。线程应该在主线程工作时休眠,并在必须再次启动时通知线程。在线程工作时,main应该以与join相同的方式等待 我不能以一个强有力的实施而告终,总是陷入僵局 简单的基线代码,任何关于如何修改它的提示都将不胜
arg
在不同的内存区域上运行函数。然后连接线程,main在区域之间执行一些数据混合,并创建n新线程,这些线程执行与旧线程相同的操作
为了改进程序,我想让线程保持活动状态,消除创建线程所需的长时间。线程应该在主线程工作时休眠,并在必须再次启动时通知线程。在线程工作时,main应该以与join相同的方式等待
我不能以一个强有力的实施而告终,总是陷入僵局
简单的基线代码,任何关于如何修改它的提示都将不胜感激
#include <thread>
#include <climits>
...
void myfunc(void * p) {
do_something(p);
}
int main(){
void * myp[n_threads] {a_location, another_location,...};
std::thread mythread[n_threads];
for (unsigned long int j=0; j < ULONG_MAX; j++) {
for (unsigned int i=0; i < n_threads; i++) {
mythread[i] = std::thread(myfunc, myp[i]);
}
for (unsigned int i=0; i < n_threads; i++) {
mythread[i].join();
}
mix_data(myp);
}
return 0;
}
#包括
#包括
...
void myfunc(void*p){
做某事(p);
}
int main(){
void*myp[n_线程]{a_位置,另一个_位置,…};
线程神话读取[n_线程];
for(无符号长整数j=0;j
您想要的概念是线程池。这涉及现有的实现
其思想是为多个线程实例提供一个容器。每个实例都与一个函数相关联,该函数轮询一个任务队列,当一个任务可用时,将其提取并运行。一旦任务结束(如果它终止,但这是另一个问题),线程将直接循环到任务队列
因此,您需要一个同步队列、一个实现队列上循环的线程类、一个用于任务对象的接口,以及一个驱动整个过程的类(pool类)
或者,您可以为它必须执行的任务创建一个非常专用的线程类(例如,仅将内存区域作为参数)。这需要线程的通知机制来指示它们是在当前迭代中完成的
线程主函数将是特定任务上的一个循环,在一次迭代结束时,线程发出结束信号,并等待条件变量启动下一个循环。本质上,您将在线程中内联任务代码,完全不需要队列
using namespace std;
// semaphore class based on C++11 features
class semaphore {
private:
mutex mMutex;
condition_variable v;
int mV;
public:
semaphore(int v): mV(v){}
void signal(int count=1){
unique_lock lock(mMutex);
mV+=count;
if (mV > 0) mCond.notify_all();
}
void wait(int count = 1){
unique_lock lock(mMutex);
mV-= count;
while (mV < 0)
mCond.wait(lock);
}
};
template <typename Task>
class TaskThread {
thread mThread;
Task *mTask;
semaphore *mSemStarting, *mSemFinished;
volatile bool mRunning;
public:
TaskThread(Task *task, semaphore *start, semaphore *finish):
mTask(task), mRunning(true),
mSemStart(start), mSemFinished(finish),
mThread(&TaskThread<Task>::psrun){}
~TaskThread(){ mThread.join(); }
void run(){
do {
(*mTask)();
mSemFinished->signal();
mSemStart->wait();
} while (mRunning);
}
void finish() { // end the thread after the current loop
mRunning = false;
}
private:
static void psrun(TaskThread<Task> *self){ self->run();}
};
classcMyTask {
public:
MyTask(){}
void operator()(){
// some code here
}
};
int main(){
MyTask task1;
MyTask task2;
semaphore start(2), finished(0);
TaskThread<MyTask> t1(&task1, &start, &finished);
TaskThread<MyTask> t2(&task2, &start, &finished);
for (int i = 0; i < 10; i++){
finished.wait(2);
start.signal(2);
}
t1.finish();
t2.finish();
}
使用名称空间std;
//基于C++11特性的信号量类
类信号量{
私人:
互斥体mMutex;
条件变量v;
int-mV;
公众:
信号量(intv):mV(v){}
无效信号(整数计数=1){
独特的锁(mMutex);
mV+=计数;
如果(mV>0)mCond.notify_all();
}
无效等待(整数计数=1){
独特的锁(mMutex);
mV-=计数;
而(mV<0)
等待(锁定);
}
};
模板
类任务线程{
线程mThread;
任务*mTask;
信号量*mSemStarting,*mSemFinished;
波动性布尔运算;
公众:
TaskThread(任务*任务,信号量*开始,信号量*完成):
mTask(任务),MRUNING(正确),
mSemStart(开始),mSemFinished(完成),
mThread(&TaskThread::psrun){}
~taskshread(){mThread.join();}
无效运行(){
做{
(*mTask)();
mSemFinished->signal();
mSemStart->wait();
}同时(运行);
}
void finish(){//结束当前循环后的线程
mRunning=假;
}
私人:
静态void psrun(TaskThread*self){self->run();}
};
类任务{
公众:
MyTask(){}
void运算符()(){
//这里有一些代码
}
};
int main(){
我的任务1;
我的任务2;
信号量开始(2),结束(0);
任务线程t1(&task1,&start,&finished);
任务线程t2(&task2,&start,&finished);
对于(int i=0;i<10;i++){
完成。等待(2);
启动信号(2);
}
t1.完成();
t2.完成();
}
上面建议的(粗略的)实现依赖于任务
类型,该类型必须提供操作符()
(即类似于函子的类)。我之前说过可以将任务代码直接合并到线程函数体中,但因为我不知道,所以我尽量将其抽象化。有一个条件变量用于线程的开始,一个用于线程的结束,都封装在信号量实例中
看到另一个答案建议使用boost::barrier
,我只能支持这个想法:如果可能的话,确保用该类替换我的信号量类,原因是,对于相同的功能集,最好依赖经过良好测试和维护的外部代码,而不是自行实现的解决方案
总而言之,这两种方法都是有效的,但前者放弃了一点点性能,转而支持灵活性。如果要执行的任务需要足够长的时间,则管理和队列同步成本可以忽略不计
更新:代码已修复并测试。用信号量代替简单的条件变量。使用屏障(只是条件变量和计数器上的方便包装器)可以轻松实现。它基本上会阻塞,直到所有N个线程都达到“屏障”。然后它再次“回收”。Boost提供了一个实现
void myfunc(void * p, boost::barrier& start_barrier, boost::barrier& end_barrier) {
while (!stop_condition) // You'll need to tell them to stop somehow
{
start_barrier.wait ();
do_something(p);
end_barrier.wait ();
}
}
int main(){
void * myp[n_threads] {a_location, another_location,...};
boost::barrier start_barrier (n_threads + 1); // child threads + main thread
boost::barrier end_barrier (n_threads + 1); // child threads + main thread
std::thread mythread[n_threads];
for (unsigned int i=0; i < n_threads; i++) {
mythread[i] = std::thread(myfunc, myp[i], start_barrier, end_barrier);
}
start_barrier.wait (); // first unblock the threads
for (unsigned long int j=0; j < ULONG_MAX; j++) {
end_barrier.wait (); // mix_data must not execute before the threads are done
mix_data(myp);
start_barrier.wait (); // threads must not start new iteration before mix_data is done
}
return 0;
}
void myfunc(void*p,boost::barrier&start\u barrier,boost::barrier&end\u barrier){
而(!stop_condition)//您需要告诉他们以某种方式停止
{
start_barrier.wait();
做某事(p);
end_barrier.wait();
}
}
int main(){
void*myp[n_线程]{a_位置,另一个_位置,…};
boost::barrier start_barrier(n_线程+1);//子线程
#include <thread>
#include <iostream>
#include <sstream>
#include <future>
#include <queue>
#include <condition_variable>
#include <mutex>
// Convenience type definition
using job = std::packaged_task<void()>;
// Some data associated to each thread.
struct thread_data
{
int id; // Could use thread::id, but this is filled before the thread is started
std::thread t; // The thread object
std::queue<job> jobs; // The job queue
std::condition_variable cv; // The condition variable to wait for threads
std::mutex m; // Mutex used for avoiding data races
bool stop = false; // When set, this flag tells the thread that it should exit
};
// The thread function executed by each thread
void thread_func(thread_data* pData)
{
std::unique_lock<std::mutex> l(pData->m, std::defer_lock);
while (true)
{
l.lock();
// Wait until the queue won't be empty or stop is signaled
pData->cv.wait(l, [pData] () {
return (pData->stop || !pData->jobs.empty());
});
// Stop was signaled, let's exit the thread
if (pData->stop) { return; }
// Pop one task from the queue...
job j = std::move(pData->jobs.front());
pData->jobs.pop();
l.unlock();
// Execute the task!
j();
}
}
// Function that creates a simple task
job create_task(int id, int jobNumber)
{
job j([id, jobNumber] ()
{
std::stringstream s;
s << "Hello " << id << "." << jobNumber << std::endl;
std::cout << s.str();
});
return j;
}
int main()
{
const int numThreads = 4;
const int numJobsPerThread = 10;
std::vector<std::future<void>> futures;
// Create all the threads (will be waiting for jobs)
thread_data threads[numThreads];
int tdi = 0;
for (auto& td : threads)
{
td.id = tdi++;
td.t = std::thread(thread_func, &td);
}
//=================================================
// Start assigning jobs to each thread...
for (auto& td : threads)
{
for (int i = 0; i < numJobsPerThread; i++)
{
job j = create_task(td.id, i);
futures.push_back(j.get_future());
std::unique_lock<std::mutex> l(td.m);
td.jobs.push(std::move(j));
}
// Notify the thread that there is work do to...
td.cv.notify_one();
}
// Wait for all the tasks to be completed...
for (auto& f : futures) { f.wait(); }
futures.clear();
//=================================================
// Here the main thread does something...
std::cin.get();
// ...done!
//=================================================
//=================================================
// Posts some new tasks...
for (auto& td : threads)
{
for (int i = 0; i < numJobsPerThread; i++)
{
job j = create_task(td.id, i);
futures.push_back(j.get_future());
std::unique_lock<std::mutex> l(td.m);
td.jobs.push(std::move(j));
}
// Notify the thread that there is work do to...
td.cv.notify_one();
}
// Wait for all the tasks to be completed...
for (auto& f : futures) { f.wait(); }
futures.clear();
// Send stop signal to all threads and join them...
for (auto& td : threads)
{
std::unique_lock<std::mutex> l(td.m);
td.stop = true;
td.cv.notify_one();
}
// Join all the threads
for (auto& td : threads) { td.t.join(); }
}
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <complex>
#include <random>
const unsigned int n_threads=4; //varying this will not (almost) change the total amount of work
const unsigned int task_length=30000/n_threads;
const float task_length_variation=task_length/n_threads;
unsigned int rep=1000; //repetitions of tasks
class t_chronometer{
private:
std::chrono::steady_clock::time_point _t;
public:
t_chronometer(): _t(std::chrono::steady_clock::now()) {;}
void reset() {_t = std::chrono::steady_clock::now();}
double get_now() {return std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::steady_clock::now() - _t).count();}
double get_now_ms() {return
std::chrono::duration_cast<std::chrono::duration<double,std::milli>>(std::chrono::steady_clock::now() - _t).count();}
};
class t_barrier {
private:
std::mutex m_mutex;
std::condition_variable m_cond;
unsigned int m_threshold;
unsigned int m_count;
unsigned int m_generation;
public:
t_barrier(unsigned int count):
m_threshold(count),
m_count(count),
m_generation(0) {
}
bool wait() {
std::unique_lock<std::mutex> lock(m_mutex);
unsigned int gen = m_generation;
if (--m_count == 0)
{
m_generation++;
m_count = m_threshold;
m_cond.notify_all();
return true;
}
while (gen == m_generation)
m_cond.wait(lock);
return false;
}
};
using namespace std;
void do_something(complex<double> * c, unsigned int max) {
complex<double> a(1.,0.);
complex<double> b(1.,0.);
for (unsigned int i = 0; i<max; i++) {
a *= polar(1.,2.*M_PI*i/max);
b *= polar(1.,4.*M_PI*i/max);
*(c)+=a+b;
}
}
bool done=false;
void task(complex<double> * c, unsigned int max, t_barrier* start_barrier, t_barrier* end_barrier) {
while (!done) {
start_barrier->wait ();
do_something(c,max);
end_barrier->wait ();
}
cout << "task finished" << endl;
}
int main() {
t_chronometer t;
std::default_random_engine gen;
std::normal_distribution<double> dis(.0,1000.0);
complex<double> cpx[n_threads];
for (unsigned int i=0; i < n_threads; i++) {
cpx[i] = complex<double>(dis(gen), dis(gen));
}
t_barrier start_barrier (n_threads + 1); // child threads + main thread
t_barrier end_barrier (n_threads + 1); // child threads + main thread
std::thread mythread[n_threads];
unsigned long int sum=0;
for (unsigned int i=0; i < n_threads; i++) {
unsigned int max = task_length + i * task_length_variation;
cout << i+1 << "th task length: " << max << endl;
mythread[i] = std::thread(task, &cpx[i], max, &start_barrier, &end_barrier);
sum+=max;
}
cout << "total task length " << sum << endl;
complex<double> c(0,0);
for (unsigned long int j=1; j < rep+1; j++) {
start_barrier.wait (); //give to the threads the missing call to start
if (j==rep) done=true;
end_barrier.wait (); //wait for the call from each tread
if (j%100==0) cout << "cycle: " << j << endl;
for (unsigned int i=0; i<n_threads; i++) {
c+=cpx[i];
}
}
for (unsigned int i=0; i < n_threads; i++) {
mythread[i].join();
}
cout << "result: " << c << " it took: " << t.get_now() << " s." << endl;
return 0;
}