iOS-GCDの学習記録(3)

7643 ワード

1.dispatch_suspend/dispatch_resume
この2つの関数は、キューの実行を一時停止または続行するために使用されます.1つのdispatchキューに複数のタスクを追加し、すべてのタスクが追加される前にタスクを実行したくない場合は、キューを一時停止させ、処理が完了してからキューを実行させることができます.

dispatch_suspend(queue);
dispatch_resume(queue);

注意すべき点は、一時停止はまだ実行を開始していないタスクの実行を禁止し、すでに実行中のタスクは一時停止できません.キューが再開されると、実行を開始していないタスクが正常に実行されます.
2.Dispatch Semaphore
短い間隔のタスクの一部を同時制御する必要がある場合、Semaphore(信号量)はシリアルキューまたはdispatch_よりもbarrier_asyncの方が使いやすいです.
前述したように、データの読み取りや更新を同時に行うと、データの競合やプログラムのクラッシュが発生しやすくなります.シリアルキューやdispatch_barrier_async関数はこの問題を回避する.しかし、短い時間間隔でいくつかの同時制御を行う必要がある場合があります.例えば、次の例です.


dispatch_queue_t queue =
	dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSMutableArray *array = [[NSMutableArray alloc] init];
for (int i = 0; i < 100000; ++i)
{
	dispatch_async(queue, ^{
		[array addObject:[NSNumber numberWithInt:i]];
	});
}

この例では、1つのグローバル同時キューにデータを配列に追加すると、複数のスレッドが配列を同時に操作します.NSMutableArraryではマルチスレッドはサポートされていないため、複数のスレッドが同時に配列を操作すると、配列内のデータが乱される可能性があります.この場合、dispatch semaphoreで解決することができます.
Dispatch semaphoreは、カウンタ付きの信号量です.これがマルチスレッドプログラミングにおけるいわゆるカウンタ信号量である.信号量は交通信号の標識に似ています.標識が出たときは行ってもいいです.基準が落ちたときは止めてください.Dispatch semaphoreはカウンタでこのフラグをシミュレートします.カウンタは0で、キューは新しいタスクの実行を一時停止し、信号を待つ.カウンタが0を超えると、キューは新しいタスクを実行し続け、カウンタを減らします.dispatch semaphoreを作成するには、dispatch_を使用します.semaphore_create関数.

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

関数のパラメータは、カウンタの初期カウントです.同様に、関数の名前に「create」という単語があるので、この関数で作成したオブジェクトは、それを必要としない後にdispatch_を使用します.リリースします.

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

dispatch_semaphore_wait関数は、1つの信号量を待つために使用されます.信号量のカウンタが1(以上)の数字になると、関数は待機を停止し、カウンタを1減らして戻ります.関数の2番目のパラメータは、指定された待機時間、dispatch_です.time_tタイプ.dispatch_semaphore_wait関数の戻り値とdispatch_group_wait関数の戻り値タイプと同様に、異なる戻り値は異なる結果を表します.以下のコードで示します.


dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC);
long result = dispatch_semaphore_wait(semaphore, time);
if (result == 0)
{
/*
 *           1。
 *              ,             1   。
 *         
 *
 *    ,            。
 */
}
else
{
/*
 *          0,        。
 * 
 *            。
 */
}

dispatch_semaphore_wait関数が0を返すと、タスクを安全に実行できます.タスクが完了したらdispatchを呼び出すsemaphore_signal関数は、信号量のカウンタに1を加算します.(注:これは少し似ています.-1はあなたが資源を占有していることを示しています.使い終わった後、+1はあなたが使い終わったことを示しています.資源が空いています.他の待っている人は信号を受け取ってから、あなたのこの空き資源を使うことができます.)
次に、前の例で信号量をどのように使用するかを見てみましょう.


dispatch_queue_t queue =
	dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

/*
 *        
 * 
 *          1,        1     NSMutableArray  。
 */
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

NSMutableArray *array = [[NSMutableArray alloc] init];
for (int i = 0; i < 100000; ++i)
{
	dispatch_async(queue, ^{
		
		/*
		 *      
		 *
		 *     ,            1。
		 */
		dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

		/*
		 *         >=1,
		 * dispatch_semaphore_wait      ,     -1,    
		 *
		 *    ,      ,        0。
		 *       1,              NSMutableArray  。
		 * 
		 *   ,   ,           。
		 */
		[array addObject:[NSNumber numberWithInt:i]];

		/*
		 *      ,   dispatch_semaphore_signal  ,    +1
		 *               ,                          。
		 */
		dispatch_semaphore_signal(semaphore);
	)};
}

/*
 *             ,      .
 */
dispatch_release(semaphore);

私が見つけた別の文章をもう一度添付しても、とてもいいです.
一連のスレッドを処理する場合、数が一定量に達すると、以前はNSOperationQueueを使用して同時制御を処理することを選択していたかもしれませんが、どのようにしてGCDで迅速に同時制御を制御するのでしょうか.答えはdispatch_semaphoreは、unix開発をよくしている人にとって、私が紹介した内容は非常にエントリーレベルに見えるかもしれませんが、信号量は彼らのマルチスレッド開発では普通です.
GCDの3つの関数はsemaphoreの操作で、それぞれ:dispatch_semaphore_create semaphore dispatch_を作成するsemaphore_Signalは信号dispatchを送信するsemaphore_wait待機信号
簡単にこの3つの関数を紹介して、第1の関数は1つの整形のパラメータがあって、私達は信号の総量と理解することができて、dispatch_semaphore_Signalは1つの信号を送信して、自然に信号の総量に1をプラスして、dispatch_semaphore_waitは信号を待っていて、信号の総量が0未満の時ずっと待っていて、さもなくば正常に実行することができて、そして信号の総量-1を譲って、このような原理によって、私達は迅速に1つの同時制御を作成することができます.


dispatch_group_t group = dispatch_group_create();
dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i < 100; i++)
{
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    dispatch_group_async(group, queue, ^{
        NSLog(@"%i",i);
        sleep(2);
        dispatch_semaphore_signal(semaphore);
    });
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group);
dispatch_release(semaphore);

このコードを簡単に紹介すると、最初に値を10にしたsemaphoreが作成され、forループのたびに新しいスレッドが作成され、スレッドが終了すると信号が送信され、スレッドが作成される前に信号が待機するので、同時に10個のスレッドが作成されるとforループがブロックされ、スレッドが終了すると1つの信号が増加して実行されます.これにより、コンカレント数が10のスレッドキューであるように、コンカレントの制御が形成される.
3.dispatch_once
dispatch_once関数は、1つのタスクがプログラムの実行中に1回しか実行されないことを確認するために使用されます.次のコードは、オブジェクトを初期化する典型的な方法です.


static int initialized = NO;
if (initialized == NO)
{
	/*
	 *      
	 */
	
	initialized = YES;
}

dispatch_を使うとonce関数を実装するとより簡潔になります.


static dispatch_once_t pred;
dispatch_once(&pred, ^{
	/*
 	 *       
	 */
});

4.Dispatch I/O
大きなファイルを読み込むとき、ファイルをいくつかの小さなブロックに分けてグローバルキューで同時に読み込むと、通常のファイル全体を一度に読み取るよりもずっと速くなると思います.現在のI/Oハードウェアでは、単一スレッドの読み取りよりも同時読み取りが速い場合があります.より高速な読み取り速度を得るには、Dispatch I/OとDispatch Dataを使用します.Dispatch I/0を使用してファイルを読み書きすると、ファイルは固定サイズのファイルブロックに分割され、グローバルキューでアクセスできます.


dispatch_async(queue, ^{/*       0   8191   。 */});
dispatch_async(queue, ^{/*       8192   16383   。 */});
dispatch_async(queue, ^{/*       16384   24575    */});
dispatch_async(queue, ^{/*       24576   32767    */});
dispatch_async(queue, ^{/*       32768   40959    */});
dispatch_async(queue, ^{/*       40960   49151    */});
dispatch_async(queue, ^{/*       49152   57343    */});
dispatch_async(queue, ^{/*       57344   65535    */});

コードに示すように、読み出しはブロック化されます.読み取ったデータをDispatch Dataで簡単に組み合わせることができます.以下に、アップル公式のdispatch I/Oとdispatch dataの例を示します.


pipe_q = dispatch_queue_create("PipeQ", NULL);
pipe_channel = dispatch_io_create(DISPATCH_IO_STREAM, fd, pipe_q, ^(int err){
        close(fd);
});

*out_fd = fdpair[1];

dispatch_io_set_low_water(pipe_channel, SIZE_MAX);

dispatch_io_read(pipe_channel, 0, SIZE_MAX, pipe_q, ^(bool done, dispatch_data_t pipedata, int err){
	if (err == 0)
	{
		size_t len = dispatch_data_get_size(pipedata);
		if (len > 0)
		{
			const char *bytes = NULL;
			char *encoded;
			dispatch_data_t md = dispatch_data_create_map(pipedata, (const void **)&bytes, &len);
			encoded = asl_core_encode_buffer(bytes, len);
			asl_set((aslmsg)merged_msg, ASL_KEY_AUX_DATA, encoded);
			free(encoded);
			_asl_send_message(NULL, merged_msg, -1, NULL);
			asl_msg_release(merged_msg);
			dispatch_release(md);
		}
}

if (done)
{
	dispatch_semaphore_signal(sem);
	dispatch_release(pipe_channel);
	dispatch_release(pipe_q);
}
});

これはアップルのシステムログAPI(Libc-763.11 gen/asl.c)からのソースコードです.dispatch_io_create関数はdispatch I/Oを作成します.エラーが発生したときに実行されるblockと、blockを実行するキューを指定します.dispatch_io_set_low_water関数は、読み取り操作ごとに最大で読み取ることができるデータの長さを指定します(データはこのサイズでブロックされます).dispatch_io_read関数は、グローバルキュー上で読み取り操作を開始します.1つのデータが読み出されるたびに、データはパラメータとしてdispatch_に渡される.io_readのコールバック関数なので、blockはデータをスキャンしたり組み合わせたりすることができます.
ファイルを読み込むときの速度が速い場合は、dispatch I/Oを試してみましょう.
参考記事:CGDにおけるマルチスレッドの同時制御GCD使用を迅速に実現するための詳細