使代码线程安全的最有效方法是什么? P>一些C++库,我正在研究一个简单的跟踪机制,它可以被激活来生成日志文件,显示哪些函数被调用,哪些参数被传递。它基本上归结为一个跟踪宏溢出到库的所有源代码中,该宏扩展为如下内容: 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 ); \ }
这个想法是人们可以在他们的代码中只说使代码线程安全的最有效方法是什么? P>一些C++库,我正在研究一个简单的跟踪机制,它可以被激活来生成日志文件,显示哪些函数被调用,哪些参数被传递。它基本上归结为一个跟踪宏溢出到库的所有源代码中,该宏扩展为如下内容: 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 ); \ },c++,multithreading,logging,trace,C++,Multithreading,Logging,Trace,这个想法是人们可以在他们的代码中只说TRACE(“foo-hit”),这将 调用调试打印函数,否则将是禁止操作。他们可以使用其他一些API(此处未显示)来配置只打印位置(源文件/行号)中使用的跟踪。此配置可以在运行时更改 问题是,现在应该在多线程代码库中使用这个想法。因此,TRACE扩展到的代码需要在同时运行代码的多个执行线程面前正常工作。目前,代码库中大约有20000个不同的跟踪点,它们经常被命中,因此它们应该相当有效 使这种方法线程安全的最有效方法是什么?我需要一个针对Windows(XP和
TRACE(“foo-hit”)
,这将
调用调试打印函数,否则将是禁止操作。他们可以使用其他一些API(此处未显示)来配置只打印位置(源文件/行号)中使用的跟踪。此配置可以在运行时更改
问题是,现在应该在多线程代码库中使用这个想法。因此,TRACE
扩展到的代码需要在同时运行代码的多个执行线程面前正常工作。目前,代码库中大约有20000个不同的跟踪点,它们经常被命中,因此它们应该相当有效
使这种方法线程安全的最有效方法是什么?我需要一个针对Windows(XP和更新版本)和Linux的解决方案。我害怕仅仅为了检查过滤器配置是否更改而进行过度锁定(99%的跟踪点被命中时,配置没有更改)。我也愿意对宏进行更大的更改。因此,与其讨论互斥与临界段性能,还可以接受的是,如果宏只是将事件发送到另一个线程中的事件循环(假设访问事件循环是线程安全的),并且所有处理都发生在同一个线程中,因此它是使用事件循环同步的
更新:我可以将这个问题简化为:
如果我有一个线程读取指针,另一个线程可能会写入变量(但99%的时间不会),我如何避免读取线程需要一直锁定?您的评论说“在运行时过滤配置更改时将其重置为零”,但我这样读对了吗“当过滤配置更改时,将其重置为零”
#define TRACE(msg) \
{ \
static TraceProc traceCallback = \
updateTraceBallback( &traceCallback, __FILE__, __LINE__ ); \
traceCallback( msg ); \
}
不知道updateTraceCallback是如何实现其数据结构的,也不知道它引用了哪些其他数据来决定何时重置回调(或者确实是首先设置回调)一个类似的问题也适用于知道traceCallback做什么——例如,如果它访问一个共享的输出目的地
考虑到这些限制,唯一不需要重新编写其他代码的安全建议是在整批代码(或者最好是Windows上的关键部分)周围粘贴一个互斥锁。您的评论说“在运行时更改过滤配置时将其重置为零”,但我这样理解正确吗当过滤配置更改时,将其重置为零
不知道updateTraceCallback是如何实现其数据结构的,也不知道它引用了哪些其他数据来决定何时重置回调(或者确实是首先设置回调)一个类似的问题也适用于知道traceCallback做什么——例如,如果它访问一个共享的输出目的地
考虑到这些限制,唯一不需要修改其他代码的安全建议是在整批代码(或者最好是Windows上的关键部分)周围粘贴一个互斥锁。我仍然不完全理解这个问题,所以请纠正我没有得到的任何问题
(我省略了反斜杠。)
我仍然不完全理解这个问题,所以请纠正我没有得到的任何东西
(我省略了反斜杠。)
我害怕仅仅为了检查过滤器配置是否更改而执行过度锁定(99%的跟踪点被命中时,配置没有更改)。我也愿意对宏进行更大的更改。因此,与其讨论互斥与关键部分性能,还可以接受宏只向不同线程中的事件循环发送事件(假设访问事件循环是线程安全的)
您认为线程之间的线程安全消息传递是如何在没有锁的情况下实现的
不管怎样,这里有一个可能可行的设计:
必须更改保存筛选器的数据结构,以便从堆中动态分配它,因为我们将创建多个筛选器实例。此外,还需要向其中添加引用计数。您需要一个类型定义,如:
typedef struct Filter
{
unsigned int refCount;
// all the other filter data
} Filter;
在某个地方声明了一个单例“当前筛选器”
static Filter* currentFilter;
并使用一些默认设置初始化
在跟踪宏中:
#define TRACE(char* msg)
{
static Filter* filter = NULL;
static TraceProc traceCallback = NULL;
if (filterOutOfDate(filter))
{
getNewCallback(__FILE__, __LINE__, &traceCallback, &filter);
}
traceCallback(msg);
}
filterOutOfDate()
只需将筛选器与currentFilter进行比较,看看是否相同。只需比较地址就足够了。它不会锁定
getNewCallback()
应用当前筛选器以获取新的跟踪函数,并使用当前筛选器的地址更新传入的筛选器。它的实现必须使用互斥锁进行保护。此外,它减少原始筛选器的refCount,并增加新筛选器的refCount。这样我们就知道何时可以释放旧筛选器r
void getNewCallback(const char* file, int line, TraceProc* newCallback, Filter** filter)
{
// MUTEX lock
newCallback = // whatever you need to do
currentFilter->refCount++;
if (*filter != NULL)
{
*filter->refCount--;
if (*filter->refCount == 0)
{
// free filter and associated resources
}
}
*filter = currentFilter;
// MUTEX unlock
}
当您想要更改过滤器时,可以执行以下操作
changeFilter()
{
Filter* newFilter = // build a new filter
newFilter->refCount = 0;
// MUTEX lock (same mutex as above)
currentFilter = newFilter;
// MUTEX unlock
}
我害怕仅仅为了检查过滤器配置是否更改而执行过度锁定(99%的跟踪点被命中时,配置没有更改)。我也愿意对宏进行更大的更改。因此,与其讨论互斥与关键部分性能,还可以接受宏只向不同线程中的事件循环发送事件(假设访问事件循环是线程安全的)
你觉得怎么样
changeFilter()
{
Filter* newFilter = // build a new filter
newFilter->refCount = 0;
// MUTEX lock (same mutex as above)
currentFilter = newFilter;
// MUTEX unlock
}
extern long trace_version;
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...