使用c ++ 11中的新std :: async,我想我可能会尝试实现一个异步版本的OutputDebugString来解除我通常大量打印每个细节所带来的性能下降通过通常的OutputDebugString函数。
所以这是我原来的同步OutputDebugString实现(可以工作):
static void OutputDebugStringN(const char *format, ...)
{
char outstring[256];
memset(outstring, 0, sizeof(outstring));
try
{
va_list args = {0};
va_start(args, format); //args = (va_list) (&format+1);
vsprintf_s(outstring, format, args);
va_end(args);
OutputDebugString(outstring);
}
catch (...) //most likely reference val arg error (va_list doesn't support ref args)
{
OutputDebugString("[OutputDebugStringN] Something went wrong\n");
}
}
以及我对非同步版本的非常有效的尝试(不起作用):
static void OutputDebugStringN(const char *format, ...)
{
auto future = std::async([]{
char outstring[256];
memset(outstring, 0, sizeof(outstring));
try
{
va_list args = {0};
va_start(args, format); //args = (va_list) (&format+1);
vsprintf_s(outstring, format, args);
va_end(args);
OutputDebugString(outstring);
}
catch (...) //most likely reference val arg error (va_list doesn't support ref args)
{
OutputDebugString("[OutputDebugStringN] Something went wrong\n");
}
});
}
由于以上方法不起作用,我现在正处于开始认为异步调用OutputDebugStringN可能比尝试在函数内部启动异步作业更好的方面(这有效,但是繁琐的):
auto dstring = std::async([]{ OutputDebugStringN("[NovelScript::ParseTokens] searched bookmark: \"%s\" does not exist\n", bookmark.c_str());} );
所以这里有两个我想问的问题:
对上述代码和任何其他评论的批评也非常受欢迎。
答案 0 :(得分:7)
我认为你应该为你的消息设置一个队列,而不是每次调用你的函数都启动一个线程,这样你的消息就会以正确的顺序输出。
所以你的功能,例如OutputDebugStringN(const char *format, ... )
将创建消息字符串,然后对单独的打印输出线程读取的字符串进行排队。那个帖子会调用OutputDebugString
。
这是一个例子 - 虽然不完整,但是不应该修改错误处理和print_from_queue直到某些终止条件运行并且对CPU更友好。
std::mutex g_m;
std::deque<std::string> que;
std::atomic<bool> endcond = false;
void queue(std::string msg)
{
std::lock_guard<mutex> _(g_m);
que.push_back(msg);
}
void print_from_queue()
{
while ( !endcond )
{
if ( que.size() )
{
std::lock_guard<mutex> _(g_m);
std::string msg = que.front();
que.pop_front();
OutputDebugStringA(msg.c_str());
}
}
}
int debugf( const char *format,... )
{
std::vector<char> line(256);
va_list args;
va_start( args, format );
int len = vsprintf_s( &line[0], line.size(), format, args );
va_end( args );
queue( &line[0] );
return len;
}
int _tmain(int argc, _TCHAR* argv[])
{
auto thr = std::async( std::launch::async, print_from_queue );
debugf("message1");
debugf("message2");
...
答案 1 :(得分:3)
在我看来,调试应该是同步的,而不是异步的。如果调试器在发生后提供异常秒数,您会感激吗?在您的程序崩溃后(因为您创建了文件写入asnyc),您是否会欣赏具有程序状态陈旧数据的日志文件?
无论如何,你继续将调试输出变为异步。它解决了什么目的,除了将数据转储到调试窗口,你知道它不是最近的,它不反映你的/用户或程序的动作,它都是陈旧的!你完全不能依赖它。
答案 2 :(得分:2)
批评上面的代码和C ++异步函数:: p
std :: async的返回值是std :: future类型的对象。由std :: async创建的std :: future的析构函数等待,直到任务执行完毕。所以当你这样做时:
auto future = std::async(...
或
auto dstring = std::async([]{
它创建了一个std :: future类型的对象,当你离开OutputDebugStringN的范围时,它会调用std :: future的析构函数,它会阻塞,直到任务执行完毕。
在我看来,这是C ++的一个缺陷。它有些愚蠢(希望这不会冒犯任何人:p),它完全违背了异步的目的。为了获得大多数人期望的行为(显然,你期望它),你必须保留std :: future对象的列表,然后花费精力(和处理时间)找出销毁个别std的正确时间: :列表中的未来对象。这是OP中问题#1的答案。对于#2,我认为你不应该在每个调试消息的基础上为此目的使用std :: async。我认为它产生的问题多于解决的问题。
我不知道这有一种优雅的方式。也许别人可以插话。
至于我如何实现OutputDebugString的异步版本,我只想创建一个生产者 - 消费者队列的字符串。有很多问题,你可以谷歌生产者 - 消费者队列的细节。生产者是发出消息的主要线程。使用者是一个线程(或几个线程),从队列中挑出元素并调用Window的OutputDebugString。
编辑:如果我冒犯了任何异步爱好者,我想补充一点,std :: async对于进行并行计算非常有用,就像使用GPU一样。我怀疑它是专为利用并行硬件而设计的。例如: // merge sort
{
auto left = std::async(.....); // merge_sort left
auto right = std::async(.....); // merge_sort right
}
merge
以这种方式,在我们合并之前,必须对左边和右边进行排序。如果没有,请等到它们都被排序,但它允许有机会同时处理左右两个。
如果您已完成CUDA或任何GPGPU编码,这应该看起来非常熟悉......