4

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());} );

だからここに私が聞きたい2つの質問があります:

  1. OutputDebugStringの非同期バージョンを実装するにはどうすればよいですか?
  2. OutputDebugStringの非同期バージョンを実装しようとさえすべきですか?

上記のコードに対する批判やその他のコメントも大歓迎です。

4

3 に答える 3

9

関数を呼び出すたびにスレッドを開始するのではなく、メッセージのキューを用意する必要があると思います。そうすれば、メッセージはクリーンで正しい順序で出力されます。

したがって、関数は、たとえば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");
...
于 2013-01-26T11:29:43.153 に答える
3

私の意見では、デバッグは非同期ではなく同期である必要があります。デバッガーが発生してから数秒後に例外が発生する場合は、よろしくお願いします。プログラムがクラッシュした後(ファイル書き込みasnycを作成したため)、プログラムステータスの古いデータを含むログファイルをいただければ幸いです。

とにかく、あなたは先に進んで、デバッグ出力を非同期にしました。データをデバッグウィンドウにダンプする以外に、どのような目的で解決しますか。最近のものではなく、ユーザーやプログラムのアクションを反映しておらず、すべて古くなっています。あなたは絶対にそれに頼ることはできません。

于 2013-01-26T11:48:48.337 に答える
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コーディングを行ったことがある場合、これは非常によく知られているはずです...

于 2013-01-26T11:40:46.297 に答える