質問で指摘したように、主な違いは、スケジューラーがスレッドをプリエンプトするかどうかです。プログラマーがデータ構造の共有や「スレッド」間の同期について考える方法は、プリエンプティブシステムと協調システムでは大きく異なります。
協調システム(多くの名前で呼ばれる、協調マルチタスク、非プリエンプティブマルチタスク、ユーザーレベルスレッド、グリーンスレッド、およびファイバーは現在5つの一般的なものです)では、プログラマーはコードがアトミックに実行される限り、アトミックに実行されることが保証されますシステムコールやを呼び出しませんyield()
。これにより、複数のファイバー間で共有されるデータ構造の処理が特に簡単になります。クリティカルセクションの一部としてシステムコールを実行する必要がない限り、クリティカルセクションにマークを付ける必要はありません(たとえば、ミューテックスlock
やunlock
呼び出しで)。したがって、次のようなコードで:
x = x + y
y = 2 * x
プログラマーは、他のファイバーがx
とy
変数を同時に処理することを心配する必要はありません。 x
そしてy
他のすべての繊維の観点からアトミック一緒に更新されます。同様に、すべてのファイバーは、ツリーのようないくつかのより複雑な構造を共有することができ、コールtree.insert(key, value)
は、ミューテックスやクリティカルセクションによって保護される必要はありません。
対照的に、プリエンプティブマルチスレッドシステムでは、真の並列/マルチコアスレッドの場合と同様に、明示的なクリティカルセクションがない限り、スレッド間で命令を可能な限りインターリーブできます。割り込みとプリエンプションは、任意の2つの命令の間になる可能性があります。上記の例では:
thread 0 thread 1
< thread 1 could read or modify x or y at this point
read x
< thread 1 could read or modify x or y at this point
read y
< thread 1 could read or modify x or y at this point
add x and y
< thread 1 could read or modify x or y at this point
write the result back into x
< thread 1 could read or modify x or y at this point
read x
< thread 1 could read or modify x or y at this point
multiply by 2
< thread 1 could read or modify x or y at this point
write the result back into y
< thread 1 could read or modify x or y at this point
したがって、プリエンプティブシステムまたは真に並列なスレッドを備えたシステムで正しく機能するためにはlock
、最初のミューテックスunlock
と最後のミューテックスのような何らかの同期ですべてのクリティカルセクションを囲む必要があります。
したがって、ファイバーは、プリエンプティブスレッドや真に並列なスレッドよりも、非同期I / Oライブラリに似ています。ファイバースケジューラが呼び出され、長い待機時間のI / O操作中にファイバーを切り替えることができます。これにより、クリティカルセクションの周りの同期操作を必要とせずに、複数の同時I / O操作の利点を得ることができます。したがって、ファイバーを使用すると、プリエンプティブスレッドまたは真に並列なスレッドよりもプログラミングの複雑さが少なくなる可能性がありますが、ファイバーを本当に同時にまたはプリエンプティブに実行しようとすると、クリティカルセクションの周りの同期の欠如は悲惨な結果につながります。