0

OS の書籍では、リーダーとライターが同時にデータにアクセスするのを防ぐためにロックが必要であると述べています。しかし、x86 マシンで簡単な例をテストすると、うまく動作します。知りたいのですが、ここのロックは必要ですか?

#define _GNU_SOURCE
#include <sched.h>

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>


struct doulnum
{
 int i;
 long int l;
 char c;
 unsigned int ui;
 unsigned long int ul;
 unsigned char uc;
};


long int global_array[100] = {0};


void* start_read(void *_notused)
{
 int i;
 struct doulnum d;
 int di;
 long int dl;
 char dc;
 unsigned char duc;
 unsigned long dul;
 unsigned int dui;
 while(1)
    {
     for(i = 0;i < 100;i ++)
        {
         dl = global_array[i];
         //di = d.i;
         //dl = d.l;
         //dc = d.c;
         //dui = d.ui;
         //duc = d.uc;
         //dul = d.ul;
         if(dl > 5 || dl < 0)
            printf("error\n");
         /*if(di > 5 || di < 0 || dl > 10 || dl < 5)
            {
             printf("i l value %d,%ld\n",di,dl);
             exit(0);
            }
         if(dc > 15 || dc < 10 || dui > 20 || dui < 15)
            {
             printf("c ui value %d,%u\n",dc,dui);
             exit(0);
            }
         if(dul > 25 || dul < 20 || duc > 30 || duc < 25)
            {
             printf("uc ul value %u,%lu\n",duc,dul);
             exit(0);
            }*/
        }
    }
}


int start_write(void)
{
 int i;
 //struct doulnum dl;
 while(1)
    {
     for(i = 0;i < 100;i ++)
        {
         //dl.i = random() % 5;
         //dl.l = random() % 5 + 5;
         //dl.c = random() % 5 + 10;
         //dl.ui = random() % 5 + 15;
         //dl.ul = random() % 5 + 20;
         //dl.uc = random() % 5 + 25;
         global_array[i] = random() % 5;
        }
    }
 return 0;
}


int main(int argc,char **argv)
{
 int i;
 cpu_set_t cpuinfo;
 pthread_t pt[3];
 //struct doulnum dl;
 //dl.i = 2;
 //dl.l = 7;
 //dl.c = 12;
 //dl.ui = 17;
 //dl.ul = 22;
 //dl.uc = 27;
 for(i = 0;i < 100;i ++)
    global_array[i] = 2;
 for(i = 0;i < 3;i ++)
     if(pthread_create(pt + i,NULL,start_read,NULL) < 0)
        return -1;
/* for(i = 0;i < 3;i ++)
        {
         CPU_ZERO(&cpuinfo);
         CPU_SET_S(i,sizeof(cpuinfo),&cpuinfo);
         if(0 != pthread_setaffinity_np(pt[i],sizeof(cpu_set_t),&cpuinfo))
                {
                 printf("set affinity %d\n",i);
                 exit(0);
                }
        }
 CPU_ZERO(&cpuinfo);
 CPU_SET_S(3,sizeof(cpuinfo),&cpuinfo);
 if(0 != pthread_setaffinity_np(pthread_self(),sizeof(cpu_set_t),&cpuinfo))
        {
         printf("set affinity recver\n");
         exit(0);
        }*/
 start_write();
 return 0;
}
4

5 に答える 5

1

読み取りと書き込みを同期させない場合、書き込み操作がアトミックでない場合、ライターが書き込みにリーダーが読み取りを行い、半分書き込まれた状態でデータを読み取る可能性があります。そうです、それが起こらないようにするには同期が必要です。

于 2012-02-24T16:10:04.797 に答える
0

ここで必ず同期が必要です。単純な理由は、start_write がグローバル配列の情報を更新し、3 つのスレッドの 1 つがグローバル配列から同じデータを読み取ろうとすると、データが一貫性のない状態になる可能性が明確にあるためです。あなたが引用したものも間違っています。「リーダーとライターが同時にアクセスすることからデータを保護するためにロックする必要があります」は「リーダーとライターによって同時にデータが変更されることを防ぐためにロックする必要があります」

共有データがスレッドの 1 つによって変更され、別のスレッドがそれを読み取っている場合は、ロックを使用して保護する必要があります。

共有データが 2 つ以上のスレッドによってアクセスされている場合、それを保護する必要はありません。

于 2012-02-24T16:21:51.080 に答える
0

スレッドが から読み取っているだけであれば問題なく動作しglobal_arrayます。 printfこれは追加モードで単一の IO 操作を行うため、問題ないはずです。

ただし、メイン スレッドは他のスレッドと同時にstart_write更新を呼び出すため、非常に予測不可能な方法で値を読み取ることになります。これは、スレッドが OS にどのように実装されているか、使用している CPU/コアの数などに大きく依存します。これは、デュアル コア開発ボックスではうまく機能するかもしれませんが、16 コアの運用サーバーに移行すると見事に失敗します。global_arraystart_read

たとえば、スレッドが同期していない場合global_array、適切な状況で更新がまったく表示されない可能性があります。または、一部のスレッドは他のスレッドよりも速く変更を認識します。メモリページが中央メモリにフラッシュされるタイミングと、スレッドがキャッシュの変更を確認するタイミングがすべてです。一貫した結果を確保するには、キャッシュを強制的に更新するための同期 (メモリ バリア) が必要です。

于 2012-02-24T16:17:32.730 に答える
0

一般的な答えは、必要な原子性を確保/強制する何らかの方法が必要であり、読者が一貫性のない状態を認識しないようにすることです。

ロック (正しく行われる) は十分ですが、常に必要というわけではありません。しかし、それが必要ではないことを証明するには、関連する操作の原子性について何かを言うことができる必要があります。

これには、ターゲット ホストのアーキテクチャと、ある程度はコンパイラの両方が関係します。

あなたの例では、配列に long を書き込んでいます。この場合、問題は長いアトミックのストレージですか? おそらくそうですが、ホストによって異なります。CPU が long (上位/下位ワード/バイト) の一部を別々に書き出す可能性があるため、リーダーは決して書き込まれない値を取得する可能性があります。(これは、最近のほとんどの CPU アーキテクチャではありそうにないと思いますが、確認する必要があります。)

CPU に書き込みバッファリングが存在する可能性もあります。これを見てから長い時間が経ちましたが、必要な書き込みバリア命令がなければ、ストアの再注文を取得できると思います。これに依存するかどうかは、あなたの例からは不明です。

最後に、コンパイラがデータが変更されていないという仮定をしないようにするために、おそらく配列にフラグを立てる必要がありますvolatile(繰り返しますが、私はしばらくこれを行っていないので、詳細については錆びています)。その下に。

于 2012-02-24T16:24:08.833 に答える