c++,如何创建线程受限/受保护的变量和函数?
Posted
技术标签:
【中文标题】c++,如何创建线程受限/受保护的变量和函数?【英文标题】:c++, how do I create thread-restricted/protected variables and functions? 【发布时间】:2019-06-19 06:00:30 【问题描述】:我正在构建的应用程序中有三个线程,所有这些线程在应用程序的生命周期内都保持打开状态。几个变量和函数只能从特定线程访问。在我的调试编译中,如果从非法线程访问这些函数或变量之一,我希望运行检查并引发错误,但我不希望这成为我最终编译的开销。我真的只是想要这个,这样我的程序员就不会犯愚蠢的错误,而不是保护我正在执行的程序不犯错误。
最初,我有一个“线程保护”类模板,它可以包装函数的返回类型,并在隐式转换为预期的返回类型之前对构造进行检查,但这似乎不适用于 void 返回类型没有禁用重要警告,也没有解决我的受保护变量问题。
有没有一种方法可以做到这一点,还是超出了语言的范围? '如果你需要这个解决方案,你做错了' cmets 不被赞赏,我设法用这种方法将我的程序的执行时间减少了近一半,但我很可能会犯一个导致无声竞赛的错误条件和最终未定义的行为。
【问题讨论】:
static_assert 对我来说似乎是解决方案。您可以提供线程 ID,并在访问通过 static_assert 检查的变量时。编译后不应该是开销 @Narase 你能给出一些示例代码吗?缺少 setter/getter 我不确定实际放置 static_assert,但我同意它至少会在其中发挥一些作用。 你能提供一个你正在做什么的例子吗?这使得回答更容易。 "如果从非法线程访问这些函数或变量之一,我希望运行检查并抛出错误," - 为什么不只是使其他线程无法访问变量?无需检查,无需开销。将线程对象包装在一个类中,并将私有成员添加到该类中。示例:***.com/questions/56588075/… @TechNeko 在它们周围放置一个包装器,例如“template您所描述的正是assert
宏的用途。
assert(condition)
在调试版本中检查condition
。如果为假,程序将在该行抛出异常。在发布版本中,assert
和括号内的任何内容都不会被编译。
如果您解释了您要保护的变量,那将更有帮助。它们是什么类型的?他们来自哪里?他们的寿命是多少?它们是全球性的吗?如果返回的类型为 void,为什么需要保护它?您是如何最终陷入一个线程可能意外访问某些内容的情况的。我不得不猜测,但我会在这里抛出一些想法:
#include <thread>
#include <cassert>
void protectedFunction()
assert(std::this_thread::get_id() == g_thread1.get_id());
// protect a global singleton (full program lifetime)
std::string& protectedGlobalString()
static std::string inst;
assert(std::this_thread::get_id() == g_thread1.get_id());
return inst;
// protect a class member
int SomeClass::protectedInt()
assert(std::this_thread::get_id() == g_thread1.get_id());
return this->m_theVar;
// thread protected wrapper
template <typename T>
class ThreadProtected
std::thread::id m_expected;
T m_val;
public:
ThreadProtected(T init, std::thread::id expected)
: m_val(init), m_expected(expected)
T Get()
assert(std::this_thread::get_id() == m_expected);
return m_val;
;
// specialization for void
template <>
class ThreadProtected<void>
public:
ThreadProtected(std::thread::id expected)
assert(std::this_thread::get_id() == expected);
;
assert
是老派。我们实际上被告知停止在工作中使用它,因为它会导致资源泄漏(异常在堆栈中被捕获)。它有可能导致调试头痛,因为调试行为与发布行为不同。很多时候,如果断言的条件是错误的,那么就没有一个好的选择。您通常不想继续运行该函数,但您也不知道要返回什么值。 assert
在开发代码时仍然非常有用。我个人一直使用assert
。
static_assert
在这里没有帮助,因为您正在检查的条件(例如“哪个线程正在运行此代码?”)是运行时条件。
另一个说明:
不要将要编译的内容放在assert
中。现在看起来很明显,但是很容易做一些像
int* p;
assert(p = new(nothrow) int); // check that `new` returns a value -- BAD!!
检查new
的分配情况很好,但分配不会发生在发布版本中,甚至在开始发布测试之前您都不会注意到!
int* p;
p = new(nothrow) int;
assert(p); // check that `new` returns a value -- BETTER...
最后,如果您在类主体或 .h 中编写受保护的访问器函数,您可以促使编译器内联它们。
更新以解决问题:
真正的问题是我应该在哪里放置断言宏?是一个 要求我为所有线程编写 setter 和 getter 受保护的变量然后将它们声明为内联并希望它们得到 在最终版本中进行了优化?
您说在访问时应该检查一些变量(仅在调试版本中),以确保正确的线程正在访问它们。因此,理论上,您需要在 每个 此类访问之前都有一个断言宏。如果只有几个地方,这很容易(如果是这种情况,您可以忽略我要说的所有内容)。但是,如果有太多地方开始违反 DRY 原则,我建议编写 getter/setter 并将 assert 放在里面(这是我随便给出的上面的例子)。但是,虽然断言在发布模式下不会增加开销(因为它是有条件编译的),但使用额外的函数(可能)会增加函数调用开销。但是,如果您将它们写在 .h 中,它们很有可能会被内联。
您对我的要求是想出一种方法来做到这一点而无需发布开销。既然我已经提到了内联,我有义务说编译器最了解。通常有编译器特定的方法来强制内联(因为允许编译器忽略 inline
关键字)。您应该在尝试内联之前分析代码。请参阅此问题的答案。 Is it good practice to make getters and setters inline?。您可以通过查看程序集轻松查看编译器是否内联函数。别担心,你不必擅长组装。只需找到调用函数并为 getter/setter 查找 call
。如果函数是内联的,您将不会看到 call
,而可能会看到 mov
。
【讨论】:
我很欣赏这些信息,当然很有帮助。但真正的问题是我在哪里放置断言宏?是否要求我为所有受线程保护的变量编写 setter 和 getter,然后将它们声明为内联并希望它们在最终版本中得到优化? 我已经更新了答案。希望它能回答这些问题以上是关于c++,如何创建线程受限/受保护的变量和函数?的主要内容,如果未能解决你的问题,请参考以下文章