2016-04-05 9 views
0

次のコードを書きました。C++非同期セグメントエラー

セグメント・フォールト:: 11

をしかし、時にはません:それは時々なります。理由を説明できますか?

私は次の質問についても興味があります。 一般的に、C++はスレッドに/をどのように割り当てますか?launch::asynclaunch::defferredの機能はどのように実行されますか? std::waitの不都合はstd::getの場合はfuture<void>の場合はありますか?

std::future<void>r1, r2, r3, r4, ret; 
//sometimes seg fault, sometimes pass 
void f(int id, int t) { 
    printf("call f(%d)\n", id); 
    int ans=0; 
    if (id == 3) { 
     printf("wait 3\n"); 
     if (r1.valid()) r1.wait(); 
    } 
    if (id == 4) { 
     printf("wait 4\n"); 
     if (r1.valid()) r1.wait(); 
    } 
    printf("start f(%d)\n",id); 
    cnt[id]++; 
    for (int i=1;i<=t;i++) { 
     ans++; 
    } 
    printf("end f(%d)\n", id); 
} 

int main() { 
    r3=async(f, 3, 1e8); 
    r4=async(f, 4, 1); 

    r1=async(f, 1, 1e8); 
    r2=async(f, 2, 1e2); 


    ret=async([&]() { r1.wait();r2.wait();r3.wait();r4.wait(); printf("cnt=%d,%d,%d,%d\n", cnt[1],cnt[2],cnt[3],cnt[4]); }); 

    return 0; 
} 
+4

'cnt'の宣言? 1ベースの配列索引付けが重要なポイントです。 –

+1

cntとは?カウント?それを数えてみませんか?それはどこにも定義されていません。意味のある変数名があれば役に立ちます。そして 'id == 4または3'ならr1を参照しています。非同期呼び出しでは、r1が初期化されているという保証はないので、r1.valid()を呼び出すとsegaultが発生する可能性があります。 –

+0

@ChristopherSchneiderデフォルトで構築された将来的に 'valid()'を呼び出すことができます。 'false'を返すだけです。 – Brian

答えて

1

それは時々取得します。

セグメント・フォールトは11

::しかし、時にはません。理由を説明できますか?

cntは、範囲外のアクセスがないように正しく宣言されているものとします。その場合、ここでの問題はstd::futureオブジェクトがスレッドセーフではないと考えているため、r1.valid()r1.wait()への呼び出しはr1に割り当てられ、mainに発生して競合します。このようなデータ競合は、未定義の動作を引き起こします。

あなたがmainの先頭に行

r1=async(f, 1, 1e8); 

を移動する必要がありますように見えます。 r1への書き込みは、r3r4std::asyncの呼び出しの前にシーケンスされます。 std::asyncの呼び出しは、fの対応する呼び出しと同期します。したがって、r1への書き込みは、r1.waitへのコールがfになる前に発生します。 future::waitメンバ関数はconstなので、2つのスレッドは競合なしに同時に呼び出すことができます。

さらに複雑なケースの場合は、std::packaged_task(最初にstd::futureを取得し、必要に応じて後で起動できます)またはstd::promiseを使用できます。一般的に

、C++はにスレッドを割り当てない方法/ C++ launch::asynclaunch::defferred機能を実行するときありませんか?

std::asyncが選択肢を与える際にどの戦略を使用するかを尋ねる場合は、答えは不定です。