gpt4 book ai didi

c++ - 使此代码线程安全的最有效方法是什么?

转载 作者:行者123 更新时间:2023-11-30 00:41:40 27 4
gpt4 key购买 nike

我正在研究的一些 C++ 库具有简单的跟踪机制,可以激活该机制以生成日志文件,显示调用了哪些函数以及传递了哪些参数。它基本上归结为遍及整个库源的 TRACE 宏,宏扩展为如下所示:

typedef void(*TraceProc)( const char *msg );

/* Sets 'callback' to point to the trace procedure which actually prints the given
* message to some output channel, or to a null trace procedure which is a no-op when
* case the given source file/line position was disabled by the client.
*
* This function also registers the callback pointer in an internal data structure
* and resets it to zero in case the filtering configuration changed since the last
* invocation of updateTraceCallback.
*/
void updateTraceCallback( TraceProc *callback, const char *file, unsinged int lineno );

#define TRACE(msg) \
{ \
static TraceProc traceCallback = 0; \
if ( !traceCallback ) \
updateTraceCallback( &traceCallback, __FILE__, __LINE__ ); \
traceCallback( msg ); \
}

这个想法是人们可以在他们的代码中说 TRACE("foo hit")调用调试打印功能,否则它将是空操作。他们可以使用一些其他 API(此处未显示)来配置仅打印位置(源文件/行号)中使用的 TRACE。此配置可以在运行时更改。

问题是这个想法现在应该用在多线程代码库中。因此,TRACE 扩展到的代码需要在多个执行线程同时运行代码的情况下正确工作。目前代码库中大约有 20.000 个不同的跟踪点,而且它们经常被命中,因此它们应该相当高效

使这种方法线程安全的最有效方法是什么?我需要一个适用于 Windows(XP 和更新版本)和 Linux 的解决方案。我害怕只是为了检查过滤器配置是否更改而进行过多锁定(99% 的时间跟踪点被命中,配置没有更改)。我也愿意对宏进行更大的更改。因此,与其讨论互斥量与临界区性能,不如宏只是将事件发送到不同线程中的事件循环(假设访问事件循环是线程安全的)并且所有处理都发生在同一个线程中,这也是可以接受的线程,因此它使用事件循环进行同步。

更新:我可以将这个问题简化为:

如果我有一个线程读取指针,而另一个线程可能写入变量(但 99% 的时间它不会),我如何避免读取线程需要一直锁定?

最佳答案

您可以实现一个配置文件版本变量。当你的程序启动时,它被设置为 0。宏可以保存一个静态整数,它是它看到的最后一个配置版本。然后,最后一次看到的配置版本和当前配置版本之间的简单原子比较将告诉您是否需要进行完全锁定并重新调用 updateTraceCallback();

那样的话,99% 的时间你只会添加一个额外的原子操作,或内存屏障或类似的东西,这是非常便宜的。 1% 的时间,只做完整的 mutex 事情,它不应该以任何明显的方式影响你的性能,如果它只有 1% 的时间。

编辑:

一些.h文件:

extern long trace_version;

一些.cpp文件:

long trace_version = 0;

宏:

#define TRACE(msg)                                                  
{
static long __lastSeenVersion = -1;
static TraceProc traceCallback = 0;
if ( !traceCallback || __lastSeenVersion != trace_version )
updateTraceCallback( &traceCallback, &__lastSeenVersion, __FILE__, __LINE__ );
traceCallback( msg );
}

增加版本和更新的函数:

static long oldVersionRefcount = 0;
static long curVersionRefCount = 0;

void updateTraceCallback( TraceProc *callback, long &version, const char *file, unsinged int lineno ) {
if ( version != trace_version ) {
if ( InterlockedDecrement( oldVersionRefcount ) == 0 ) {
//....free resources.....
//...no mutex needed, since no one is using this,,,
}
//....aquire mutex and do stuff....
InterlockedIncrement( curVersionRefCount );
*version = trace_version;
//...release mutex...
}
}

void setNewTraceCallback( TraceProc *callback ) {
//...aquire mutex...
trace_version++; // No locks, mutexes or anything, this is atomic by itself.
while ( oldVersionRefcount != 0 ) { //..sleep? }
InterlockedExchange( &oldVersionRefcount, curVersionRefCount );
curVersionRefCount = 0;
//.... and so on...
//...release mutex...

当然,这非常简单,因为如果您需要升级版本和 oldVersionRefCount > 0,那您就有麻烦了;如何解决这个问题取决于您,因为这实际上取决于您的问题。我的猜测是,在这些情况下,您可以简单地等到引用计数为零,因为引用计数递增的时间应该是运行宏所需的时间。

关于c++ - 使此代码线程安全的最有效方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3211463/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com