2

@synchronizedディレクティブがどのように機能するかを確認するために、このマルチスレッドアプリケーションを実行しています。すべてのスレッドが@synchronizedの引数と同じオブジェクトを持っている場合、それらはすべて同じロックで待機することを読みました。すべてのスレッドで同じであるため、引数として。
このアプリケーションでは、すべてのスレッドによって複数回編集されているテキストフィールドがあります。パフォーマンスは気にしません。これは単なるテストなので、@ synchronizedディレクティブをforの前ではなく、その中に入れます。

私が使用するプロパティ:

@property (weak) IBOutlet NSTextField *textField;
@property (nonatomic, copy) NSNumber* value;
@property (nonatomic,copy) NSMutableArray* threads;

コード:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    value= @0;
    textField.objectValue= value;
    for(int i=0; i<10; i++)
    {
        NSThread* thread=[[NSThread alloc]initWithTarget: self selector: @selector(routine:) object: nil];
        [threads addObject: thread];
        [thread start];
    }
}

- (void) routine : (id) argument
{
    for(NSUInteger i=0; i<100; i++)
    {
        @synchronized(self)
        {
            value= @(value.intValue+1);
            textField.objectValue= value;
        }
    }
}

アプリケーションが成功し、テキストフィールドの値として1000が表示される場合もありますが、そうでない場合もありますが、これが飢餓であり、テキストフィールドに何も表示されず、空です。デバッグを試しましたが、何が表示されているのかわかりません。間違っています。失敗の基準は私にはさりげなく思えるので、うまくいくこともあります。

解決

@synthesize threads,value, textField;

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    value= @0;
    threads=[[NSMutableArray alloc]initWithCapacity: 100];
    for(NSUInteger i=0; i<100; i++)
    {
        NSThread* thread=[[NSThread alloc]initWithTarget: self selector: @selector(routine:) object: nil ];
        [threads addObject: thread];
        [thread start];
    }
}

- (void) routine : (id) arg
{
    for(NSUInteger i=0; i<1000; i++)
    {
        @synchronized(self)
        {
            value= @(value.intValue+1);
            [textField performSelectorOnMainThread: @selector(setObjectValue:) withObject: value waitUntilDone: NO];
        }
    }
}
4

2 に答える 2

3

非メインスレッドから、NSTextFieldのサブクラスであるにアクセスしています。それは安全なことではありません。結果は未定義です。うまくいくように見えることもあれば、うまくいかないこともあります。NSView

于 2012-11-23T22:34:45.953 に答える
1

常にバックグラウンドスレッドでUIを更新しています。これは良くない。あなたはそれを次のようにすべきです。

 (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    value= @0;
    textField.objectValue= value;
    for(int i=0; i<10; i++)
    {
        NSThread* thread=[[NSThread alloc] initWithTarget: self selector: @selector(routine:) object: nil];
        [threads addObject: thread];
        [thread start];
    }
}

- (void) routine : (id) argument
{
    for(NSUInteger i=0; i<100; i++)
    {
        @synchronized(self)
        {
            value= @(value.intValue+1);
            [textField performSelectorOnMainThread:@selector(setObjectValue:) withObject:value waitUntilDone: NO];    
        }
    }
}

インスタンスをロックし、sleepメソッドを使用してよりスムーズにします。

バックグラウンドスレッド内の変数をロックするには、最初に変数をロックしてから、その値を設定し、次のようにロックを解除します。

[NSLock lock];
value=@(value.intValue+1)
[NSLock unlock];

ただし、すでに@synchronizedを使用しているため、複数のスレッドから同時にアクセスされる変数を保護できます。@synchonizedブロックの方が理解しやすいと思います。

この場合、睡眠がより適切です。スレッド内にたとえば2秒のスリープを設定すると、textFieldが2秒ごとに変化するのがわかり、より見やすく、より意味のあるものになります。

[NSThread sleepForTimeInterval:2000]// textFieldを更新した後、これをループ内に配置して効果を確認します。

また、runloopを作成し、いくつかのrunloopでスレッドを実行することをお勧めします。これはより良い方法です。

于 2012-11-23T22:40:29.703 に答える