我正在使用pthreads
将C程序转移到C ++,并且为了使程序多平台,可移植等,必须大量使用Boost库。
过去使用线程时,我的代码通常会有以下形式:
void threadFunc(void* pUserData)
{
if ( !pUserData )
{
return;
}
myStruct* pData = (myStruct*)pUserData;
bool bRun;
lock(pUserData);
bRun = pUserData->bRun;
unlock(pUserData);
while ( bRun )
{
// Do some stuff
// ...
// ...
lock(pUserData);
bRun = pUserData->bRun;
unlock(pUserData);
}
/* Done execution of thread; External thread must have set pUserData->bRun
* to FALSE.
*/
}
这正如我所料。当我希望线程关闭时,我只需锁定它访问struct
的互斥锁(通常是struct
的成员),切换bRun
标志,然后线程上join()
。对于提升线程,我已经注意到我的代码在执行非阻塞操作时timed_join()
超时similar to this SO question的情况。这让我怀疑我没有正确使用增强线程。
首先,两个通用线程结构中的哪一个对于让线程正确捕获thread_interrupted
异常是正确的?
void threadFunc( void* pUserData )
{
while ( 1 )
{
try
{
// Do some stuff
// ...
// ...
boost::this_thread::sleep(boost::posix_time::milliseconds(1));
}
catch(boost::thread_interrupted const& )
{
// Thread interrupted; Clean up
}
}
}
void threadFunc( void* pUserData )
{
try
{
while ( 1 )
{
// Do some stuff
// ...
// ...
boost::this_thread::sleep(boost::posix_time::milliseconds(1));
}
}
catch(boost::thread_interrupted const& )
{
// Thread interrupted; Clean up
}
}
第二,如果我希望线程有机会来捕获中断调用,那么调用代替睡眠的适当的boost函数是什么,而不是sleep()
或者放弃当前持有的CPU时间片的剩余部分? From another SO question on this topic,似乎boost::this_thread::interruption_point()
电话可能是我正在寻找的,但我不能100%确定它是否会一直有效,从我在SO中读到的内容问题我从中引用了它。
最后,我的理解是,在我的循环中不调用任何方式的boost sleep()
函数或某些类似的中断点函数将意味着{{1}将永远超时,我要么:
这个假设是否正确?
谢谢。
timed_join()
,访问2016-01-18。<https://stackoverflow.com/questions/7316633/boost-thread-how-to-acknowledge-interrupt>
,访问2016-01-18。答案 0 :(得分:3)
您的代码不是例外安全的。因此,当您等待加入时,您很容易死锁。
如果在持有互斥锁时收到异常,则代码永远不会解锁互斥锁,可能会导致等待线程死锁。
要检查的事项:
首先,您的代码不会在任何地方显示您如何创建尝试中断的线程。
您需要在boost::thread
- 受管线程实例上运行它。
这应该是显而易见的,因为没有这样的对象就很难调用boost::thread::interrupt()
。尽管如此,我还是要仔细检查这是有序的,因为线程函数的函数签名强烈建议使用本机POSIX pthread_create
。
一个好主意是检查
interrupt_request()
(will befalse
for native threads)的返回值:当从使用本机接口创建的线程调用时,
boost::this_thread
中断相关函数在降级模式下运行,即boost::this_thread::interruption_enabled()
返回false。因此,使用boost::this_thread::disable_interruption
和boost::this_thread::restore_interruption
将不会执行任何操作,只会忽略对boost::this_thread::interruption_point()
的调用。
如何实施锁定/解锁功能?同样在这里也是如此。如果您与POSIX / non-boost API混合使用,那么您可能会错过信号。
在您使用时,请开始使用lock_guard
或unique_lock
,以便您的代码安全。
关于您的Case A
vs Case B
问题,案例B最简单。
案例A未显示退出while
循环的计划。你当然可以使用break
,return
(甚至是goto
),但是当你展示它时,它将是一个无限循环。
除了sleep()
之外,还有yield()
明确放弃了剩余的时间片,同时又是一个中断点。
我知道你已经说过你不想放弃时间片,但是我不确定你是否意识到这会导致线程烧掉CPU,如果没有工作要做:
让我觉得非常矛盾,你希望不要放弃剩余的时间片(表示低延迟,高带宽应用程序和无锁上下文)而你正在谈论的同时使用mutex
。显然,后者比仅仅yield
部分时间片贵得多,因为它是无锁并发的相反。
因此,让我总结两个好的风格演示,一个锁定和一个无锁(或锁定保守,取决于你的线程同步逻辑的其余部分)。
这使用“旧”线程控件,因为它在锁定时很好,IMO:
<强> Live On Coliru 强>
#include <boost/thread.hpp>
void trace(std::string const& msg);
struct myStruct {
bool keepRunning = true;
using mutex_t = boost::mutex;
using lock_t = boost::unique_lock<mutex_t>;
lock_t lock() const { return lock_t(mx); }
private:
mutex_t mutable mx;
};
void threadFunc(myStruct &userData) {
trace("threadFunc enter");
do
{
{
auto lock = userData.lock();
if (!userData.keepRunning)
break;
} // destructor of unique_lock unlocks, exception safe
// Do some stuff
// ...
// ...
trace("(work)");
boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
} while (true);
trace("threadFunc exit");
}
int main() {
trace("Starting main");
boost::thread_group threads;
constexpr int N = 4;
std::vector<myStruct> data(N);
for (int i=0; i < N; ++i)
threads.create_thread(boost::bind(threadFunc, boost::ref(data[i])));
boost::this_thread::sleep_for(boost::chrono::seconds(1));
trace("Main signaling shutdown");
for (auto& d : data) {
auto lock = d.lock();
d.keepRunning = false;
}
threads.join_all();
trace("Bye");
}
void trace(std::string const& msg) {
static boost::mutex mx;
boost::lock_guard<boost::mutex> lk(mx);
static int thread_id_gen = 0;
thread_local int thread_id = thread_id_gen++;
std::cout << "Thread #" << thread_id << ": " << msg << "\n";
}
输出,例如
Thread #0: Starting main
Thread #1: threadFunc enter
Thread #1: (work)
Thread #2: threadFunc enter
Thread #2: (work)
Thread #3: threadFunc enter
Thread #3: (work)
Thread #4: threadFunc enter
Thread #4: (work)
Thread #3: (work)
Thread #1: (work)
Thread #2: (work)
Thread #4: (work)
Thread #1: (work)
Thread #3: (work)
Thread #4: (work)
Thread #2: (work)
Thread #4: (work)
Thread #1: (work)
Thread #3: (work)
Thread #2: (work)
Thread #3: (work)
Thread #4: (work)
Thread #2: (work)
Thread #1: (work)
Thread #2: (work)
Thread #1: (work)
Thread #4: (work)
Thread #3: (work)
Thread #2: (work)
Thread #4: (work)
Thread #1: (work)
Thread #3: (work)
Thread #4: (work)
Thread #2: (work)
Thread #1: (work)
Thread #3: (work)
Thread #4: (work)
Thread #2: (work)
Thread #1: (work)
Thread #3: (work)
Thread #4: (work)
Thread #2: (work)
Thread #1: (work)
Thread #3: (work)
Thread #0: Main signaling shutdown
Thread #4: threadFunc exit
Thread #2: threadFunc exit
Thread #1: threadFunc exit
Thread #3: threadFunc exit
Thread #0: Bye
上一个锁定示例的字面翻译:
<强> Live On Coliru 强>
但是,现在拥有一个共享的关闭标志可能更有意义:
<强> Live On Coliru 强>
#include <boost/thread.hpp>
void trace(std::string const& msg);
struct myStruct {
int value;
};
void threadFunc(myStruct userData, boost::atomic_bool& keepRunning) {
std::string valuestr = std::to_string(userData.value);
trace("threadFunc enter(" + valuestr + ")");
do
{
if (!keepRunning)
break;
// Do some stuff
// ...
// ...
trace("(work" + valuestr + ")");
boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
} while (true);
trace("threadFunc " + valuestr + " exit");
}
int main() {
boost::atomic_bool running { true };
trace("Starting main");
boost::thread_group threads;
constexpr int N = 4;
for (int i=0; i < N; ++i) {
threads.create_thread(
boost::bind(threadFunc, myStruct{i}, boost::ref(running)
));
}
boost::this_thread::sleep_for(boost::chrono::seconds(1));
trace("Main signaling shutdown");
running = false;
threads.join_all();
trace("Bye");
}
void trace(std::string const& msg) {
static boost::mutex mx;
boost::lock_guard<boost::mutex> lk(mx);
static int thread_id_gen = 0;
thread_local int thread_id = thread_id_gen++;
std::cout << "Thread #" << thread_id << ": " << msg << "\n";
}
请注意,使用threadFunc
¹
boost::bind
的方式更加“自然C ++”
使用interruption_point()
请注意interrupt()
州的文档:
效果:如果
*this
引用执行的线程,请求thread
下次进入其中一个时将被中断 预定义的中断点启用中断,或者如果是 当前在调用其中一个预定义的中断点时被阻止 启用中断 [强调我的]
值得注意的是,可以暂时禁用中断。 Boost仅为此提供启用RAII的类,因此默认设置为异常安全。如果您的代码不使用class disable_interruption
or class restore_interruption
,则您不必担心这一点。
<强> Live On Coliru 强>
#include <boost/thread.hpp>
void trace(std::string const& msg);
struct myStruct {
int value;
};
void threadFunc(myStruct userData) {
std::string valuestr = std::to_string(userData.value);
trace("threadFunc enter(" + valuestr + ")");
// using `Case B` form from your question
try {
do
{
boost::this_thread::sleep_for(boost::chrono::milliseconds(100)); // Avoids huge output for demo
trace("(work" + valuestr + ")");
boost::this_thread::interruption_point();
} while (true);
} catch(boost::thread_interrupted const& tie) {
trace("threadFunc " + valuestr + " interrupted");
}
trace("threadFunc " + valuestr + " exit");
}
int main() {
trace("Starting main");
boost::thread_group threads;
constexpr int N = 4;
for (int i=0; i < N; ++i) {
threads.create_thread(boost::bind(threadFunc, myStruct{i}));
}
boost::this_thread::sleep_for(boost::chrono::seconds(1));
trace("Main signaling shutdown");
threads.interrupt_all();
threads.join_all();
trace("Bye");
}
void trace(std::string const& msg) {
static boost::mutex mx;
boost::lock_guard<boost::mutex> lk(mx);
static int thread_id_gen = 0;
thread_local int thread_id = thread_id_gen++;
std::cout << "Thread #" << thread_id << ": " << msg << "\n";
}
¹或lambdas如果你喜欢