在 C++ 11 中延迟启动线程
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25524775/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Delayed start of a thread in C++ 11
提问by Smithy
I'm getting into C++11 threads and have run into a problem.
我正在使用 C++11 线程并遇到了问题。
I want to declare a thread variable as global and start it later.
我想将一个线程变量声明为全局变量并稍后启动它。
However all the examples I've seen seem to start the thread immediately for example
但是,例如,我看到的所有示例似乎都立即启动了线程
thread t(doSomething);
What I want is
我想要的是
thread t;
and start the thread later.
并稍后启动线程。
What I've tried is
我试过的是
if(!isThreadRunning)
{
thread t(readTable);
}
but now t is block scope. So I want to declare t and then start the thread later so that t is accessible to other functions.
但现在 t 是块作用域。所以我想声明 t ,然后稍后启动线程,以便其他函数可以访问 t 。
Thanks for any help.
谢谢你的帮助。
回答by Sam Varshavchik
std::thread
's default constructor instantiates a std::thread
without starting or representing any actual thread.
std::thread
的默认构造函数实例化 astd::thread
而不启动或表示任何实际线程。
std::thread t;
The assignment operator moves the state of a thread object, and sets the assigned-from thread object to its default-initialized state:
赋值运算符移动线程对象的状态,并将分配自线程对象设置为其默认初始化状态:
t = std::thread(/* new thread code goes here */);
This first constructs a temporary thread object representing a new thread, transfers the new thread representation into the existing thread object that has a default state, and sets the temporary thread object's state to the default state that does not represent any running thread. Then the temporary thread object is destroyed, doing nothing.
这首先构造一个表示新线程的临时线程对象,将新线程表示转移到具有默认状态的现有线程对象中,并将临时线程对象的状态设置为不代表任何正在运行的线程的默认状态。然后临时线程对象被销毁,什么也不做。
Here's an example:
下面是一个例子:
#include <iostream>
#include <thread>
void thread_func(const int i) {
std::cout << "hello from thread: " << i << std::endl;
}
int main() {
std::thread t;
std::cout << "t exists" << std::endl;
t = std::thread{ thread_func, 7 };
t.join();
std::cout << "done!" << std::endl;
}
回答by antred
I would give the thread a condition variable and a boolean called startRunning(initially set to false). Effectively you would start the thread immediately upon creation, but the first thing it would do is suspend itself (using the condition_variable) and then only begin processing its actual task when the condition_variable is signaled from outside (and the startRunningflag set to true).
我会给线程一个条件变量和一个名为startRunning的布尔值(最初设置为 false)。实际上,您将在创建时立即启动线程,但它会做的第一件事是挂起自身(使用 condition_variable),然后仅在从外部发出 condition_variable 信号(并且startRunning标志设置为 true)时才开始处理其实际任务。
EDIT: PSEUDO CODE:
编辑:伪代码:
// in your worker thread
{
lock_guard l( theMutex );
while ( ! startRunning )
{
cond_var.wait( l );
}
}
// now start processing task
// in your main thread (after creating the worker thread)
{
lock_guard l( theMutex );
startRunning = true;
cond_var.signal_one();
}
EDIT #2: In the above code, the variables theMutex, startRunningand cond_varmust be accessible by both threads. Whether you achieve that by making them globals or by encapsulating them in a struct / class instance is up to you.
编辑#2:在上面的代码中,变量theMutex、startRunning和cond_var必须可由两个线程访问。是通过将它们设为全局变量还是通过将它们封装在 struct / class 实例中来实现这一点取决于您。
回答by Igor
first declared in class m_grabber
runs nothing. We assign member class object with new one with lambda function in launch_grabber
method and thread with lambda runs within source
class context.
首先在课堂上声明m_grabber
什么都不运行。我们在launch_grabber
方法中使用 lambda 函数为成员类对象分配新的对象,并且使用 lambda 的线程在source
类上下文中运行。
class source {
...
std::thread m_grabber;
bool m_active;
...
}
bool source::launch_grabber() {
// start grabber
m_grabber = std::thread{
[&] () {
m_active = true;
while (true)
{
if(!m_active)
break;
// TODO: something in new thread
}
}
};
m_grabber.detach();
return true;
}
回答by CashCow
There is no "standard" of creating a thread "suspended" which I assume is what you wanted to do with the C++ thread library. Because it is not supported on every platform that has threads, it is not there in the C++ API.
没有创建“挂起”线程的“标准”,我认为这就是您想要使用 C++ 线程库执行的操作。因为并非每个具有线程的平台都支持它,所以它不在 C++ API 中。
You might want to create a class with all the data it is required but not actually run your thread function. This is not the same as creating the thread but may be what you want. If so, create that, then later bind the object and its
operator()
orstart()
function or whatever to the thread.You might want the thread id for your thread. That means you do actually need to start the thread function. However it can start by waiting on a condition variable. You then signal or broadcast to that condition variable later when you want it to continue running. Of course you can have the function check a condition after it resumes in case you might have decided to close it and not run it after all (in which case it will just return instantly).
You might want a
std::thread
object with no function. You can do that and attach it to a function later torun
that function in a new thread.
您可能希望使用所需的所有数据创建一个类,但实际上并不运行您的线程函数。这与创建线程不同,但可能是您想要的。如果是这样,创建,然后再绑定的对象和它的
operator()
或start()
功能或什么的线程。您可能需要线程的线程 ID。这意味着您确实需要启动线程函数。然而,它可以从等待条件变量开始。然后,当您希望它继续运行时,您稍后会向该条件变量发出信号或广播。当然,您可以让函数在恢复后检查条件,以防您决定关闭它而不运行它(在这种情况下它会立即返回)。
您可能想要一个
std::thread
没有功能的对象。您可以这样做,然后将其附加到run
新线程中的函数。
回答by Sasha
As antred says in his answer, you can use a condition variable to make the thread to wait in the beginning of its routine.
正如 antred 在他的回答中所说,您可以使用条件变量使线程在其例程开始时等待。
Scott Meyers in his book “Effective Modern C++” (in the “Item 39: Consider void
futures for one-shot event communication”) proposes to use void
-future instead of lower level entities (boolean flag, conditional variable and mutex). So the problem can be solved like this:
Scott Meyers 在他的《Effective Modern C++》一书中(在“Item 39:考虑void
一次性事件通信的未来”)建议使用void
-future 而不是较低级别的实体(布尔标志、条件变量和互斥锁)。所以问题可以这样解决:
auto thread_starter = std::promise<void>;
auto thread = std::thread([starter_future = thread_starter.get_future()]() mutable {
starter_future.wait(); //wait before starting actual work
…; //do actual work
});
…; //you can do something, thread is like “paused” here
thread_starter.set_value(); //“start” the thread (break its initial waiting)
Scott Meyers also warns about exceptions in the second …
(marked by the you can do something, thread is like “paused” here
comment). If thread_starter.set_value()
is never called for some reasons (for example, due to exception throws in the second …
), the thread will wait forever, and any attempt to join it would result in deadlock.
Scott Meyers 还警告了第二个中的异常…
(由you can do something, thread is like “paused” here
评论标记)。如果thread_starter.set_value()
由于某些原因从未调用过(例如,由于在第二个中抛出异常…
),线程将永远等待,并且任何加入它的尝试都将导致死锁。
As both ways (condvar-based and future-based) contain hidden unsafety, and the first way (condvar-based) needs some boilerplate code, I propose to write a wrapper class around std::thread
. Its interface should be similar to the one of std::thread
(except that its instances should be assignable from other instances of the same class, not from std::thread
), but contain additional void start()
method.
由于这两种方式(基于 condvar 和基于 Future 的)都包含隐藏的不安全性,而第一种方式(基于 condvar)需要一些样板代码,我建议围绕std::thread
. 它的接口应该类似于其中之一std::thread
(除了它的实例应该可以从同一类的其他实例分配,而不是从std::thread
),但包含额外的void start()
方法。
Future-based thread-wrapper
基于未来的线程包装器
class initially_suspended_thread {
std::promise<bool> starter;
std::thread impl;
public:
template<class F, class ...Args>
explicit initially_suspended_thread(F &&f, Args &&...args):
starter(),
impl([
starter_future = starter.get_future(),
routine = std::bind(std::forward<F>(f), std::forward<Args>(args)...)
]() mutable {if (starter_future.get()) routine();})
{}
void start() {starter.set_value(true);}
~initially_suspended_thread() {
try {starter.set_value(false);}
catch (const std::future_error &exc) {
if (exc.code() != std::future_errc::promise_already_satisfied) throw;
return; //already “started”, no need to do anything
}
impl.join(); //auto-join not-yet-“started” threads
}
…; //other methods, trivial
};
Condvar-based thread-wrapper
基于 Condvar 的线程包装器
class initially_suspended_thread {
std::mutex state_mutex;
enum {INITIAL, STARTED, ABORTED} state;
std::condition_variable state_condvar;
std::thread impl;
public:
template<class F, class ...Args>
explicit initially_suspended_thread(F &&f, Args &&...args):
state_mutex(), state(INITIAL), state_condvar(),
impl([
&state_mutex = state_mutex, &state = state, &state_condvar = state_condvar,
routine = std::bind(std::forward<F>(f), std::forward<Args>(args)...)
]() {
{
std::unique_lock state_mutex_lock(state_mutex);
state_condvar.wait(
state_mutex_lock,
[&state]() {return state != INITIAL;}
);
}
if (state == STARTED) routine();
})
{}
void start() {
{
std::lock_guard state_mutex_lock(state_mutex);
state = STARTED;
}
state_condvar.notify_one();
}
~initially_suspended_thread() {
{
std::lock_guard state_mutex_lock(state_mutex);
if (state == STARTED) return; //already “started”, no need to do anything
state = ABORTED;
}
impl.join(); //auto-join not-yet-“started” threads
}
…; //other methods, trivial
};
回答by GreenScape
You could use singleton pattern. Or I would rather say antipattern.
您可以使用单例模式。或者我宁愿说antipattern。
Inside a singleton you would have std::thread
object encapsulated. Upon first access to singleton your thread will be created and started.
在单例中,您将std::thread
封装对象。首次访问单例时,您的线程将被创建并启动。