- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我正在使用 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()
在线程上。对于 boost 线程,我注意到我的代码中有 timed_join()
的情况。执行非阻塞操作时超时,similar to this SO question .这让我怀疑我没有正确使用 boost 线程。
首先,两种通用线程结构中的哪一种对于让线程正确捕获 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 sleep()
还是放弃当前占有的剩余CPU时间片? From another SO question on this topic , 似乎 boost::this_thread::interruption_point()
call 可能是我正在寻找的东西,但我不能 100% 确定它是否总是有效,根据我在 SO 问题中阅读的内容,我从中引用了它。
最后,据我了解,不调用任何方式的 boost sleep()
我循环中的函数或一些类似的中断点函数将意味着 timed_join()
总是会超时,我要么必须:
这个假设是否正确?
谢谢。
<https://stackoverflow.com/questions/7316633/boost-thread-how-to-acknowledge-interrupt>
, 于 2016 年 1 月 18 日访问。<https://stackoverflow.com/questions/26103648/boostthis-threadinterruption-point-doesnt-throw-boostthread-interrupted>
, 于 2016 年 1 月 18 日访问。最佳答案
您的代码不是异常安全的。因此,在等待加入时很容易出现死锁。
如果您在持有互斥量时收到异常,您的代码将永远不会解锁互斥量,可能会导致等待线程中出现死锁。
要检查的事情:
首先,您的代码不会在任何地方显示您如何创建您试图中断的线程。
您需要在boost::thread
管理的线程实例上运行它。这应该是显而易见的,因为如果没有这样的对象,将很难调用 boost::thread::interrupt()
。尽管如此,我还是想仔细检查一下这是按顺序进行的,因为线程函数的函数签名强烈建议使用 native POSIX pthread_create
。
A good idea is to check the return value of
interrupt_request()
(which will befalse
for native threads):The
boost::this_thread
interrupt related functions behave in a degraded mode when called from a thread created using the native interface, i.e.boost::this_thread::interruption_enabled()
returns false. As consequence the use ofboost::this_thread::disable_interruption
andboost::this_thread::restore_interruption
will do nothing and calls toboost::this_thread::interruption_point()
will be just ignored.
锁定/解锁功能是如何实现的?这里同样如此。如果您混合使用 POSIX/非 boost API,那么您可能会错过信号。
当你这样做的时候,开始使用 lock_guard
或 unique_lock
这样你的代码就异常安全了。
关于您的Case A
与Case B
问题,Case B 最简单。
案例 A 没有显示如何计划退出 while
循环。您当然可以使用 break
、return
(甚至 goto
),但正如您展示的那样,这将是一个无限循环。
除了 sleep()
之外,还有 yield()
明确放弃时间片的剩余部分,同时作为中断点。
我知道你说过你不想放弃时间片,但我不确定你是否意识到如果没有工作要做,这将导致线程消耗 CPU:
让我觉得非常矛盾的是,您希望不放弃剩余的时间片(表示低延迟、高带宽应用程序和无锁上下文)并且您在谈论同时使用 mutex
。显然,后者比仅 yield
部分时间片要昂贵得多,因为它是无锁并发的对立面。
因此,让我以两个良好风格的演示作为结束,一个是有锁的,一个是无锁的(或保守锁,取决于您的线程同步逻辑的其余部分)。
这使用“旧”线程控制,因为它在锁定时没问题,IMO:
#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
前面lockful例子的直译:
但是,现在拥有一个共享的关闭标志可能更有意义:
#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";
}
请注意,我们可以使用 boost::bind
¹
使用interruption_point()
请注意 interrupt()
的文档状态:
Effects: If
*this
refers to a thread of execution, request that thethread
will be interrupted the next time it enters one of the predefined interruption points with interruption enabled, or if it is currently blocked in a call to one of the predefined interruption points with interruption enabled [emphasis mine]
值得注意的是,可以暂时禁用中断。 Boost 只为此提供支持 RAII 的类,因此默认情况下是异常安全的。如果您的代码不使用 class disable_interruption
or class restore_interruption
您不必为此担心。
#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";
}
¹ 或 lambda,如果您愿意的话
关于c++ - Boost threads - 处理线程中断的安全/保证方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34859533/
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 这个问题似乎不是关于 a specific programming problem, a softwar
有没有办法保证您的系统托盘图标被删除? 添加系统托盘图标: Shell_NotifyIcon(NIM_ADD, &m_tnd); 删除系统托盘图标: Shell_NotifyIcon(NIM_DELE
是否保证(-x) % m,其中x和m在c++中为正standard (c++0x) 为负数,等于 -(x % m)? 我知道它在我知道的所有机器上都是正确的。 最佳答案 除了Luchian的回答,这是
可能还有其他方法可以作为示例,但这不是我要问的重点。 我正在这样做: (future (clojure.java.shell/sh "sleep" "3" :dir "/tmp")) 启动对Shell
可以使用 XREAD(或者可能是另一个命令)以原子方式检测数据是否写入 Redis 流? 进一步来说: 假设您在一个进程中将一些数据添加到 Redis 流中,并看到数据已通过某个自动生成的 key 成
Kotlin 协程是否提供任何“发生之前”保证? 例如,在这种情况下,写入 mutableVar 和随后在(可能)其他线程上读取之间是否存在“发生之前”保证: suspend fun doSometh
我正在开发一个跟踪行程的应用程序。在搜索了这件事之后,我得出结论,实现这一点(持续跟踪用户的位置)的最好方法是使用前台服务。在某些情况下工作得很好,但在其他一些情况下(即使关闭 DOZE),我得到一些
我正在使用 ORM (sqlalchemy) 从 PG 数据库中获取数据。我想避免在我手工编写的 SQL 语句中指定所有表列名称*。 到目前为止,我的假设是返回的列按照用于创建数据库表的 DDL 语句
在 setState 的文档中这样说: setState() does not immediately mutate this.state but creates a pending state tr
我有一个与不同硬件接口(interface)的简单应用程序。对于每个硬件,我针对一个独特的监视器函数生成了一个 pthread_t,总共有 6 个线程:1 个管理线程和 5 个工作线程。 每个线程都有
目前,我有 private ThreadLocal shortDateFormat = new ThreadLocal() { @Override protected DateFormat i
我有一个使用 SolrCloud 将文档写入 Solr 的 Java 作业。输入数据被转换为不同实体的映射,然后将每个实体写入与其实体类型对应的 Solr 集合。 我的代码如下: public voi
我们使用嵌入式设备通过串行到以太网转换器将数据包从串行端口发送到服务器。我们使用的一家制造商 Moxa 将始终以与构建它们相同的方式发送数据包。意思是,如果我们构建一个大小为 255 的数据包,它将始
我是从 C++ 转到 Java 的。在 C++ 世界中,我们关注异常安全,并注意到变元器可以在变元器本身或其委托(delegate)的方法抛出异常时提供不同的保证(最小、强、不抛出)。实现具有强异常保
我想将来自 SAAJ 的 SOAPConnectionFactory 和 MessageFactory 类与多个线程一起使用,但事实证明我不能假设它们是线程安全的。一些相关的帖子: javax.xml
关闭。这个问题是opinion-based .它目前不接受答案。 想要改进这个问题? 更新问题,以便 editing this post 可以用事实和引用来回答它. 关闭 5 年前。 Improve
关于正确性,我找不到以下代码片段没有设计缺陷的证据/反证据。 template class MyDirtyPool { public: template std::size_t ad
对于这个问题,我找到了不同的答案,我知道一定有一个确定的答案。 C 中四种主要数据类型的最小分配内存大小是多少? int , double , float , 和 char是我在想什么。做 signe
我正在使用 Kafka Producer,我的应用程序将具有相同键的各个 ProducerRecords 发送到单个分区中,然后这些 ProducerRecords 在发送到代理之前进行批处理(使用
您好,我是服务器端编程 (java) 的新手,正在阅读 SendRedirect 与 Forward 之间的区别。来自 Post-redirect-get pattern它解释说这将阻止通过点击刷新按
我是一名优秀的程序员,十分优秀!