記事のディレクトリ
I.はじめに
Linuxシステムでは、複数のスレッドが並行して実行される場合、同じリソースにアクセスする必要がある場合は、オペレーティングシステムが提供する同期プリミティブを使用して、リソースにアクセスする場所を保護する必要があります。同期プリミティブには、ミューテックス、条件変数、セマフォなどが含まれます。保護されたコードは「クリティカルセクション」と呼ばれます。
これは非常に正式なプロセスであり、基本的にこれを行います。
これらの同期プリミティブがコードの実行効率にどの程度の影響を与えるのか疑問に思ったことはありませんか?それは可能です使用しないオペレーティングシステムが提供するこれらのメカニズムを、それ以外に使用する純粋なソフトウェアのクリティカルセクションを保護する目的を達成するための方法を?
この記事では、実用的ではないかもしれないピーターソンアルゴリズムを紹介しますが、それは私たちにいくつかの思考をもたらし、プログラミングメタスキルを向上させることができます。
2.ピーターソンアルゴリズムの概要
このアルゴリズムは、主にクリティカルセクションの保護問題を解決するために使用されます。クリティカルセクションは次の3つの条件を保証する必要があることを私たちは知っています。
- 相互に排他的なアクセス:一度に1つのスレッドのみがクリティカルセクションに入ることができます。
- アイドルレットイン:クリティカルセクションでコードを実行しているスレッドがない場合は、適用するスレッドの1つを選択してクリティカルセクションに入り、クリティカルセクションに入らせる必要があります。
- 制限付き待機:スレッドがクリティカルセクションに入るために適用される場合、スレッドは無期限に待機することはできず、限られた時間内にクリティカルセクションに入る許可を取得する必要があります。言い換えれば、その優先度がどれほど低くても、臨界域の入り口で飢えて死ぬべきではありません。
Petersonのアルゴリズムは、相互排他ロックを実装する並行プログラミングアルゴリズムであり、2つのスレッドを制御して、アクセスの競合なしに共有ユーザーリソースにアクセスできます。
Petersonのアルゴリズムは、2スレッドの相互排他的アクセスのLockOneおよびLockTwoアルゴリズムに基づいています。
- LockOneアルゴリズムはフラグブール配列を使用します
- LockTwoはターン整数を使用します
これら2つのアルゴリズムはどちらも相互排除を実現しますが、どちらもデッドロックの可能性があります。Petersonのアルゴリズムは、これら2つのアルゴリズムを組み合わせて、ソフトウェアでデュアルスレッド相互排除問題を完全に実装します。
アルゴリズムは次のように説明されます
[外部リンク画像の転送に失敗しました。ソースサイトにヒル防止リンクメカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします(img-1aTmiPkc-1617241079938)(http://iottown.sewain100.cn/iot32_01) ]
2つの重要なグローバル変数:
フラグ配列:2つのブール要素があり、それぞれスレッドがクリティカルエリアに入るのに適用されるかどうかを表します。
ターン:両方のスレッドがクリティカルエリアに入るのに適用される場合、この変数はクリティカルエリアに入るスレッドを決定します。
3、テストコード
// 被 2 个线程同时访问的全局资源
static int num = 0;
BOOL flag[2] = { 0 };
int turn = 0;
void *thread0_routine(void *arg)
{
for (int i = 0; i < 1000000; ++i)
{
flag[0] = TRUE;
turn = 1;
while (TRUE == flag[1] && 1 == turn);
// 临阶区代码
num++;
flag[0] = FALSE;
}
return NULL;
}
void *thread1_routine(void *arg)
{
for (int i = 0; i < 1000000; ++i)
{
flag[1] = TRUE;
turn = 0;
while (TRUE == flag[0] && 0 == turn);
// 临阶区代码
num++;
flag[1] = FALSE;
}
return NULL;
}
グローバルリソースnumの初期値は0で、2つのプログラムはそれぞれ100万回インクリメントされるため、最終結果は200万になり、実際のテスト結果も真になります。
第4に、Mutexミューテックスロックがコード実行効率に与える影響
1.シングルスレッドの場合:コード実行効率に対するミューテックスミューテックスの影響
for (int i = 0; i < 1000000; ++i)
{
num++;
}
上記のコードは約1.8ms-3.5msかかります。
for (int i = 0; i < 1000000; ++i)
{
pthread_mutex_lock(&mutex);
num++;
pthread_mutex_unlock(&mutex);
}
上記のコードは約23.9ms〜38.9msかかります。ロックとロック解除がコード実行効率に与える影響は依然として非常に明白であることがわかります。
2.マルチスレッドの場合:コード実行効率に対するミューテックスミューテックスの影響
void *thread0_routine(void *arg)
{
for (int i = 0; i < 1000000; ++i)
{
pthread_mutex_lock(&mutex);
num++;
pthread_mutex_unlock(&mutex);
}
return NULL;
}
void *thread1_routine(void *arg)
{
for (int i = 0; i < 1000000; ++i)
{
pthread_mutex_lock(&mutex);
num++;
pthread_mutex_unlock(&mutex);
}
return NULL;
}
時間がかかる:
thread0:diff = 125.8ms
thread1:diff = 129.1ms
3. 2つのスレッドで、ピーターソンのアルゴリズムを使用してクリティカルセクションを保護します
時間がかかる:
thread1:diff = 1.89ms
thread0:diff = 1.94ms
五数要約
Petersonのアルゴリズムは、純粋なソフトウェアを使用してクリティカルセクションを保護します。これは、オペレーティングシステムによって提供されるミューテックスを使用するよりも優れたパフォーマンスを示します。
ただし、欠点もあります。2つのスレッドでしか使用できませんが、プラットフォームとは関係がないため、特別な場合には、使用できる場合があります。
[C言語]
1。C言語ポインター-基本的な原則から高度なスキルまで、完全に説明するのに役立つ画像とコード付き
2.元のgdbの基本的なデバッグ原則はとてもシンプルです
3.ステップバイステップの分析-Cを使用してオブジェクト指向プログラミングを実現する
4.コード強制を改善するための武器:マクロ定義-エントリから放棄まで
5. C言語でsetjmpとlongjmpを使用して、例外のキャプチャとルーチンを実装する
【アプリケーション設計】
1.ソフトウェアアーキテクチャを階層化してモジュールに分割する必要があるとの意見とその方法(1)
2。ソフトウェアアーキテクチャを階層化してモジュールに分割する必要があるとの意見とその方法(2)
3。IoT
ゲートウェイ開発:MQTTメッセージバスベースの設計プロセス(パート1)4。IoTゲートウェイ開発:MQTTメッセージバスベースの設計プロセス(パート2)
5。プロセス間の私のお気に入りの通信方法-メッセージバス
【オペレーティングシステム】
1.なぜ宇宙船やミサイルは、組み込みシステムではなくマイクロコントローラーを好むのですか?
[
モノのインターネット] 1。暗号化と証明書に関するもの
2.LUAスクリプト言語を深め、デバッグの原理を完全に理解できるようにする
[ナンセンス] 1。私の失敗したキャリア経験に基づく
:職場に不慣れな技術者のためのいくつかのヒント