2016-07-08 8 views
0

簡単なコード(私はそれが非常に悪いものであることを知っているが、私はちょうど例のためにそれを作った):マルチスレッド。共通のリソースを等しく共有する方法

1 #include <mutex> 
2 #include <iostream> 
3 #include <thread> 
4 
5 std::mutex mu; 
6 
7 void myFunc(void) { 
8   for (int i = 0; i < 100; ++i) { 
9     mu.lock(); 
10     std::cout << "CHILD Thread: " << std::this_thread::get_id() << std::endl; 
11     mu.unlock(); 
12   } 
13 } 
14 
15 int main() 
16 { 
17   std::thread thr(myFunc); 
18   for (int i = 0; i < 100; ++i) { 
19     mu.lock(); 
20     std::cout << "MAIN Thread: " << std::this_thread::get_id() << std::endl; 
21     mu.unlock(); 
22   } 
23   thr.join(); 
24   return 0; 
25 } 

リターン出力のこの種:

1  MAIN Thread: 140581832210240 
2  MAIN Thread: 140581832210240 
3  MAIN Thread: 140581832210240 
4  MAIN Thread: 140581832210240 
5  MAIN Thread: 140581832210240 
6  MAIN Thread: 140581832210240 
7  MAIN Thread: 140581832210240 
8  CHILD Thread: 140581814855424 
9  CHILD Thread: 140581814855424 
10  CHILD Thread: 140581814855424 
11  CHILD Thread: 140581814855424 
12  CHILD Thread: 140581814855424 
13  CHILD Thread: 140581814855424 
14  CHILD Thread: 140581814855424 
15  CHILD Thread: 140581814855424 
16  CHILD Thread: 140581814855424 
17  CHILD Thread: 140581814855424 
18  MAIN Thread: 140581832210240 
19  MAIN Thread: 140581832210240 
20  MAIN Thread: 140581832210240 
21  MAIN Thread: 140581832210240 
22  MAIN Thread: 140581832210240 
23  CHILD Thread: 140581814855424 
24  CHILD Thread: 140581814855424 
25  CHILD Thread: 140581814855424 
26  CHILD Thread: 140581814855424 
27  CHILD Thread: 140581814855424 
28  CHILD Thread: 140581814855424 
29  CHILD Thread: 140581814855424 
      ....... and so on 

と私はそれを見るように - この出力は、あるスレッドが他のスレッドを長時間待たなければならないため、マルチスレッドの意味を打ち消します。この出力は、同時に私に子供の叫び声、メインの叫び声、子供の叫び声、メインの叫び声などを与えるはずです。私はミューテックスが共通の資源の公平な分担に責任を負わないことを知っていますが、誰ですか?そして私はそれを私のプログラムにどのように実装しますか?

ありがとうございます。

EDIT:パッティングのstd :: coutの機能へ:

10 void common_cout(string msg) { 
11   mu.lock(); 
12   std::cout << msg << std::endl; 
13   mu.unlock(); 
14 } 

doesntのヘルプ。

+1

あなたはすべてのスレッドで同じメモリ空間を使用しないでください。このため、ロックなどを使用する必要があります。最初のスレッドがメモリの1/number_of_threadsを使用するように、スペースを等分します。その場合は、ロック機構を使用する必要はありません。 CPU上で実行できるスレッド数も確認してください。 CPUがサポートするスレッドよりも多くのスレッドを実行するのは理にかなっていません。 – nosbor

答えて

1

元のコードは同じ問題をウィンドウで持っていますが、ネイティブウィンドウ同等物を使用するように切り替えました。このウィンドウの例は、2つのスレッドを交互に使用しています。 ReleaseMutex()の各インスタンスは、「他の」スレッドがミューテックスを取得して実行しています。メインのスリープ(2)は、myFuncが最初にループを開始したことを確認する簡単な方法でした。

また、メインスレッドと2スレッドの合計3スレッドのバージョンを作成しました。サイクリングは順調に進んでいるので、Windowsネイティブのmutexロックは要求された順序で実行されているようです。

スレッドまたはプロセス間の一般的な同期化またはラウンドロビン型の場合、スレッドまたはプロセスごとに1つのセマフォを使用する方が、どのスレッドまたはプロセスもセマフォを増分(解放/シグナル)できるため、この問題は、セマフォーが標準スレッドインターフェースのネイティブな部分ではなく、セマフォーに相当​​するものを実装するためにmutexと条件変数の組み合わせが必要であることです。 Windowsとposixはネイティブセマフォをサポートしています。

#include <iostream> 
#include <windows.h> 

static HANDLE mu;      // handle: mutex 
static HANDLE ht1;      // handle: thread 1 
static DWORD id1;      // thread 1 id 

DWORD WINAPI myFunc(LPVOID) { 
    for (int i = 0; i < 20; ++i) { 
     WaitForSingleObject(mu, INFINITE); 
     std::cout << "child thread: " << i << std::endl; 
     ReleaseMutex(mu); 
    } 
    return 0; 
} 

int main() 
{ 
    mu = CreateMutex(NULL,TRUE,NULL); // main owns mutex 
    ht1 = CreateThread(NULL, 0, myFunc, 0, 0, &id1); 
    Sleep(2);       // make sure myFunc running 
    ReleaseMutex(mu);     // release mutex 
    for (int i = 0; i < 20; ++i) { 
     WaitForSingleObject(mu, INFINITE); 
     std::cout << "main thread: " << i << std::endl; 
     ReleaseMutex(mu); 
    } 
    WaitForSingleObject(ht1, INFINITE); 
    CloseHandle(ht1); 
    CloseHandle(mu); 
    return 0; 
} 

出力

child thread: 0 
main thread: 0 
child thread: 1 
main thread: 1 
... 
child thread: 18 
main thread: 18 
child thread: 19 
main thread: 19 

3スレッドの例:

#include <iostream> 
#include <windows.h> 

static HANDLE mu;      // handle: mutex 
static HANDLE ht0;      // handle: thread 0 
static HANDLE ht1;      // handle: thread 1 
static DWORD id0;      // thread 0 id 
static DWORD id1;      // thread 1 id 

DWORD WINAPI Thread0(LPVOID) { 
    for (int i = 0; i < 10; ++i) { 
     WaitForSingleObject(mu, INFINITE); 
     std::cout << "Thread0 : " << i << std::endl; 
     ReleaseMutex(mu); 
    } 
    return 0; 
} 

DWORD WINAPI Thread1(LPVOID) { 
    for (int i = 0; i < 10; ++i) { 
     WaitForSingleObject(mu, INFINITE); 
     std::cout << "Thread1 : " << i << std::endl; 
     ReleaseMutex(mu); 
    } 
    return 0; 
} 

DWORD WINAPI Thread2(LPVOID) { 
    for (int i = 0; i < 10; ++i) { 
     WaitForSingleObject(mu, INFINITE); 
     std::cout << "Thread2 : " << i << std::endl; 
     ReleaseMutex(mu); 
    } 
    return 0; 
} 

int main() 
{ 
    mu = CreateMutex(NULL,TRUE,NULL); // main owns mutex 
    ht0 = CreateThread(NULL, 0, Thread0, 0, 0, &id0); 
    ht1 = CreateThread(NULL, 0, Thread1, 0, 0, &id1); 
    Sleep(2);       // let other threads get started 
    ReleaseMutex(mu);     // release mutex 
    Thread2(0); 
    WaitForSingleObject(ht0, INFINITE); 
    WaitForSingleObject(ht1, INFINITE); 
    CloseHandle(ht0); 
    CloseHandle(ht1); 
    CloseHandle(mu); 
    return 0; 
} 

出力

Thread0 : 0 
Thread1 : 0 
Thread2 : 0 
Thread0 : 1 
Thread1 : 1 
Thread2 : 1 
... 
Thread0 : 9 
Thread1 : 9 
Thread2 : 9 
+0

厳密に決定論的なラウンドロビンの振る舞いについては、スレッドを生成する点はほとんどありません。単一のスレッドでループ内のコードを実行し、最初に1つの作業関数を呼び出し、次にもう1つの関数を呼び出します。 –

+1

@JeremyFriesner - この例では説明されていませんが、共有リソースとは無関係な重要なコードが存在し、マルチスレッドやマルチプロセッシングが役に立ちます。私が取り組んだWindowsと組み込みシステムの場合、マルチスレッド通信と同期は、各メッセージキューのミューテックスとセマフォと共に、メッセージキューのリンクリストを使用するスレッド間メッセージングシステムを介して行われます。 – rcgldr

1

私はミューテックスが一般的なリソースの公平な分担については責任を負わないことを知っていますが、誰ですか?

実際のスケジューリングはオペレーティングシステムによって行われます。

これは何も言っていませんが、ではなく、ではなく、非効率的なためスイッチ間の切り替えが頻繁に行われます(スイッチには多少のコストがかかります)。

言い換えれば、 "公平"という考え方 - 恐らく各スレッドが交代する厳密なラウンドロビンは、高価なデフォルトの動作になります。あなたが望むものなら、それを明示的にコーディングすることができます。公平スケジューラの通常の目標は、実行可能スレッドがどれくらい待たなければならないかと、スレッドがまだ有用な作業をしている間にスレッドを先取りすることが妥当であるかどうかのトレードオフです。

OSの動作は、もちろん、使用しているコアの数によって異なります。あなたはこれについても言及していません。

...どうすれば私のプログラムに実装できますか?

あなたのスレッドで実際の作業量を適切に行う場合は、スケジューラが好きなように動作することがあります。この種の人工的なテストでは、特にタイトなループで少量のコードを実行しているため、有用な結果を得ることはめったにありません。

+1

スレッドが別々のコア上で実行されている場合は、スレッドは常に切り替えなくても同時に、同時に実行される必要があります。この問題は、std :: coutがコンパイラ/ OSによってどのように処理されるか、またはコンパイラ/ OSがロックとロック解除をどのように処理するかに関連している可能性があります。 – rcgldr

+0

スレッドはロックの同じカップルをスラッシングすることを除いてほとんど何もしていませんので、実際には非常に頻繁に "同時に実行"されることはほとんどありません。彼らが並行して行うことができるのはforループのインクリメントとテストですが、すべての書式設定作業はmutexによって保護されています(おそらくmutexのロックを解除するよりもまだ安いかもしれません) – Useless

+1

スリープ中のスレッドは、スレッドのロックが解除されます。すでに実行中のスレッドがmutexを再度ロックする前に、実際にスケジューリングを行うために、約{mutex unlock syscall exit pathと約3ループ命令}のウィンドウがあります。実行中のスレッドが実際にI/O(フラッシングstdout)を行うまで、実際には非常に頻繁に進展することはありません。 – Useless

関連する問題