web-dev-qa-db-ja.com

Grand Central Dispatchでdispatch_syncを使用する

誰でもGCDdispatch_syncの目的が本当に明確なユースケースで説明できますか?これを使用する必要がある場所と理由を理解できません。

ありがとう!

74
Rasputin Jones

ブロックを実行して結果を待つ場合に使用します。

この一例は、同期のためにロックの代わりにディスパッチキューを使用するパターンです。たとえば、共有NSMutableArray aがあり、アクセスがディスパッチキューqによって仲介されているとします。フォアグラウンドスレッドが最初のアイテムを(同期的に)引き出している間に、バックグラウンドスレッドが配列に追加される(非同期)場合があります。

NSMutableArray *a = [[NSMutableArray alloc] init];
// All access to `a` is via this dispatch queue!
dispatch_queue_t q = dispatch_queue_create("com.foo.samplequeue", NULL);

dispatch_async(q, ^{ [a addObject:something]; }); // append to array, non-blocking

__block Something *first = nil;            // "__block" to make results from block available
dispatch_sync(q, ^{                        // note that these 3 statements...
        if ([a count] > 0) {               // ...are all executed together...
             first = [a objectAtIndex:0];  // ...as part of a single block...
             [a removeObjectAtIndex:0];    // ...to ensure consistent results
        }
});
76
David Gelhar

最初にその兄弟dispatch_asyncを理解する

//Do something
dispatch_async(queue, ^{
    //Do something else
});
//Do More Stuff

dispatch_asyncを使用して、新しいスレッドを作成します。その場合、現在のスレッドは停止しません。これは、//Do More Stuffが終了する前に//Do something elseが実行される可能性があることを意味します

現在のスレッドを停止させたい場合はどうなりますか?

ディスパッチはまったく使用しません。普通にコードを書いてください

//Do something
//Do something else
//Do More Stuff

ここで、[〜#〜] different [〜#〜]スレッドで何かをしたいのに、あたかも待つようにして、処理が完了したことを確認します連続

これには多くの理由があります。たとえば、UIの更新はメインスレッドで行われます。

dispatch_syncを使用する場所です

//Do something
dispatch_sync(queue, ^{
    //Do something else
});
//Do More Stuff

ここでは、//Do somethingが別のスレッドで実行されていても、//Do something else//Do More stuff//Do something elseが連続して実行されています。

通常、人々が異なるスレッドを使用する場合、全体の目的は、待機せずに何かを実行できるようにすることです。大量のデータをダウンロードしたいが、UIをスムーズに保ちたいとします。

したがって、dispatch_syncはほとんど使用されません。しかし、それはそこにあります。私は個人的にそれを使ったことはありません。 dispatch_syncを使用するサンプルコードまたはプロジェクトを要求しないのはなぜですか。

77
user4951

dispatch_syncは、従来の相互排他ロックと意味的に同等です。

dispatch_sync(queue, ^{
    //access shared resource
});

と同じ働きをします

pthread_mutex_lock(&lock);
//access shared resource
pthread_mutex_unlock(&lock);
25
Catfish_Man

David Gelhar 静かにシリアルキューを作成したためだけに彼の例が機能することは言わなかった(dispatch_queue_serialと等しいものをdispatch_queue_createにNULLを渡した)。

(マルチスレッドのパワーをすべて得るために)コンカレントキューを作成する場合、彼のコードは、ミューテーション中のNSArrayミューテーション(addObject :)(removeObjectAtIndex :)、または不正アクセス(NSArrayの範囲外の範囲)のためにクラッシュします。その場合、バリアを使用して、両方のブロックの実行中にNSArrayに排他的にアクセスする必要があります。 NSArrayの実行中に他のすべての書き込みを除外するだけでなく、他のすべての読み取りも除外するため、変更が安全になります。

並行キューの例は次のようになります。

NSMutableArray *a = [[NSMutableArray alloc] init];
// All access to `a` is via this concurrent dispatch queue!
dispatch_queue_t q = dispatch_queue_create("com.foo.samplequeue", DISPATCH_QUEUE_CONCURRENT);

// append to array concurrently but safely and don't wait for block completion
dispatch_barrier_async(q, ^{ [a addObject:something]; }); 

__block Something *first = nil;
// pop 'Something first' from array concurrently and safely but wait for block completion...
dispatch_barrier_sync(q, ^{                        
        if ([a count] > 0) {               
             first = [a objectAtIndex:0];  
             [a removeObjectAtIndex:0];    
        }
});
// ... then here you get your 'first = [a objectAtIndex:0];' due to synchronised dispatch.
// If you use async instead of sync here, then first will be nil.
4

実用的なサンプルが必要な場合は、私の質問をご覧ください。

不定期に発生するこのデッドロックをどのように解決しますか?

メインのManagedObjectContextがメインスレッドで作成されるようにすることで解決します。プロセスは非常に高速であり、私は待つことを気にしません。待たないということは、多くの並行性の問題に対処しなければならないことを意味します。

いくつかのコードをメインスレッドで実行する必要があるため、dispatch_syncが必要です。メインスレッドは、コードを実行するスレッドとは異なるスレッドです。

したがって、基本的にコードを1にする場合は、通常どおりに実行します。競合状態について心配する必要はありません。先に進む前に、コードが完了していることを確認する必要があります。 2.別のスレッドで完了

dispatch_syncを使用します。

1に違反している場合は、dispatch_asyncを使用します。 2に違反する場合は、通常のようにコードを記述します。

これまでのところ、私はこれを一度だけ、つまりメインスレッドで何かを行う必要がある場合にのみ行います。

コードは次のとおりです。

+(NSManagedObjectContext *)managedObjectContext {


    NSThread *thread = [NSThread currentThread];
    //BadgerNewAppDelegate *delegate = [BNUtilitiesQuick appDelegate];
    //NSManagedObjectContext *moc = delegate.managedObjectContext;

    if ([thread isMainThread]) {
        //NSManagedObjectContext *moc = [self managedObjectContextMainThread];
        return [self managedObjectContextMainThread];
    }
    else{
        dispatch_sync(dispatch_get_main_queue(),^{
            [self managedObjectContextMainThread];//Access it once to make sure it's there
        });
    }

    // a key to cache the context for the given thread
    NSMutableDictionary *managedObjectContexts =[self thread].managedObjectContexts;

    @synchronized(self)
    {
        if ([managedObjectContexts objectForKey:[self threadKey]] == nil ) {
            NSManagedObjectContext *threadContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
            threadContext.parentContext = [self managedObjectContextMainThread];
            //threadContext.persistentStoreCoordinator= [self persistentStoreCoordinator]; //moc.persistentStoreCoordinator;//  [moc persistentStoreCoordinator];
            threadContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
            [managedObjectContexts setObject:threadContext forKey:[self threadKey]];
        }
    }


    return [managedObjectContexts objectForKey:[self threadKey]];
}
3
user4951

dispatch_syncは主にdispatch_asyncブロック内で使用され、メインスレッドでいくつかの操作(update uiなど)を実行します。

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    //Update UI in main thread
    dispatch_sync(dispatch_get_main_queue(), ^{
      self.view.backgroundColor = color;
    });
});
2
Hari Kunwar

これが中途半端な現実的な例です。並行して分析したい2000個のZipファイルがあります。しかし、Zipライブラリはスレッドセーフではありません。したがって、Zipライブラリに触れるすべての作業はunzipQueueキューに入ります。 (例はRubyですが、すべての呼び出しはCライブラリに直接マップされます。たとえば、 dispatch_apply(3) にマップされる「適用」

#!/usr/bin/env macruby -w

require 'rubygems'
require 'Zip/zipfilesystem'

@unzipQueue = Dispatch::Queue.new('ch.unibe.niko.unzipQueue')
def extractFile(n)
    @unzipQueue.sync do
        Zip::ZipFile.open("Quelltext.Zip") {   |zipfile|
            sourceCode = zipfile.file.read("graph.php")
        }
    end
end

Dispatch::Queue.concurrent.apply(2000) do |i|
   puts i if i % 200 == 0
   extractFile(i)
end
0
nes1983