2

Grand Central Dispatch をテストするための小さなテスト プロジェクトを作成しました。シリアル キューを使用する必要があります。バックグラウンド タスクの実行中に、スレッドを完全に中断、再開、またはキャンセルする必要があります。そして: 作成されたキューが既に実行されているかどうかはどうすればわかりますか? (その後、再起動する必要があります)。

マルチスレッドを使用するのは初めてなので、正しく使用できているかどうかのヒントを得ることができれば本当にうれしいです. 私はそのようなものを見つけられなかったので、私のコードをチェックしていただければ幸いです. オブジェクトを正しく解放しますか? さらなる改善はありますか?

あなたの助けと時間をありがとう。

コードまたはサンプル プロジェクトへのリンクを次に示します

ViewController.m

#import "ViewController.h"
#import "SVProgressHUD.h"
#import "Queue.h"

@interface ViewController (){
    Queue* queue;
}

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    queue = [[Queue alloc] init];

    UIButton* startbutton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 
    [startbutton setTitle:@"Start Queue" forState:UIControlStateNormal];
    [startbutton addTarget:self action:@selector(startQueueButton:) forControlEvents:UIControlEventTouchUpInside];
    [startbutton setFrame:CGRectMake(100, 200, 100, 70)];
    [self.view addSubview:startbutton];


    UIButton* suspendbutton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 
    [suspendbutton setTitle:@"Stop Queue" forState:UIControlStateNormal];
    [suspendbutton addTarget:self action:@selector(suspendQueueButton:) forControlEvents:UIControlEventTouchUpInside];
    [suspendbutton setFrame:CGRectMake(250, 200, 100, 70)];
    [self.view addSubview:suspendbutton];

    UIButton* resumebutton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 
    [resumebutton setTitle:@"Resume Queue" forState:UIControlStateNormal];
    [resumebutton addTarget:self action:@selector(resumeQueueButton:) forControlEvents:UIControlEventTouchUpInside];
    [resumebutton setFrame:CGRectMake(400, 200, 170, 70)];
    [self.view addSubview:resumebutton];

    UIButton* cancelbutton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 
    [cancelbutton setTitle:@"Cancel Queue" forState:UIControlStateNormal];
    [cancelbutton addTarget:self action:@selector(cancelQueueButton:) forControlEvents:UIControlEventTouchUpInside];
    [cancelbutton setFrame:CGRectMake(600, 200, 170, 70)];
    [self.view addSubview:cancelbutton];

}

-(void) startQueueButton:(UIButton*) button{    
    NSLog(@"---> startQueueButton");
    [queue start];
}

-(void) suspendQueueButton:(UIButton*) button{
    NSLog(@"---> suspendQueueButton");
    [queue suspend];
}


-(void) resumeQueueButton:(UIButton*) button{
    NSLog(@"---> resumeQueueButton");
    [queue resume];
}

-(void) cancelQueueButton:(UIButton*) button{
    NSLog(@"---> cancelQueueButton");
    [queue cancel];
}



- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return YES;
}

@end

Queue.m

#import "Queue.h"
#import "SVProgressHUD.h"

@interface Queue (){
    dispatch_queue_t queue;
}

@end

@implementation Queue


-(void) start{
    NSLog(@"Queue - start");    

    int count = 1000;

     // SERIAL QUEUE ======================================
     // =======================================================================

    queue = dispatch_queue_create("com.jf.TestQueue", NULL);

    [SVProgressHUD showWithStatus:@"Rendering..."];


    for(int i = 0; i < count; i++) 
    {
        dispatch_async(queue, ^{

            NSLog(@"--> ASYNC %d", i);

           // rendering complete, get back to main queue
           dispatch_async(dispatch_get_main_queue(), ^
              {
                 NSLog(@"--> Image rendered: %d", i);

                  if (i == count-1) {
                      NSLog(@"EndRenderingQueue");

                      [SVProgressHUD dismiss];
                  }
              });
        });
    }

    dispatch_release(queue);    // even under ARC we have to release it    
}


-(void) suspend{

    NSLog(@"Queue - suspend");

    if (queue) {
        NSLog(@"___suspend");
        dispatch_suspend(queue);
    }
}

-(void) resume{
    NSLog(@"Queue - resume");
    if (queue) {
        dispatch_resume(queue);
    }
}
-(void) cancel{
    NSLog(@"Queue - cancel");

    if (queue) {
        dispatch_suspend(queue);
        //dispatch_release(queue);  // if it´s uncommented, it crashes. How to release it securely?
        queue = nil;

        [SVProgressHUD dismiss];

    }

}

@end
4

1 に答える 1

4

一般的には、絶対に必要でない限り、バックグラウンド キューを一時停止しないことが一般的だと思います (つまり、作成したキューが後続のブロックの実行を許可されている場合、適切に/うまく動作しない別のキューで動作している何かがあります)。これを行いたい場合もありますが、通常は気にする必要はありません。私たちの大半は、キューを作成して使用し、アクティブに使用していないときはアイドル状態にし (ただし、キューを一時停止していません)、バックグラウンド キューが再び必要になったときに再び使用し続け、すべてが完了したら使用します。それらを使用して (つまり、近い将来にバックグラウンド キューは必要ありません)、それらを解放します。その時点で、その古いキュー ポインターは再び使用されません。

停止中かどうかを知る方法としては、 以外に方法はないと思いますdispatch_debug()。中断しているので、中断されているかどうかは既にわかっているので、独自のラッパーを作成して、独自の中断カウントを追跡できます。一般的には、必要に応じて一時停止し、競合するフォアグラウンド (またはその他の) タスクが完了し、バックグラウンド キューが安全に再び使用できるようになったときに再開すると考えていました。このシナリオでは、中断されているかどうかを判断することは、より学術的な問題です。

バックグラウンド キューを一時停止する必要があるのはなぜですか? 解決しようとしているビジネス ロジックの問題は何ですか? ところで、そのキューで実行中の現在のブロックは停止の影響を受けないことをご存知だと思います。現在のブロックではなく、キューに入れられた(以前にキューに入れられて待機中、または後でキューに入れられた)ブロックのみが影響を受けると思います。

コードに関しては、start メソッドでキューを解放しています (たとえば、キュ​​ーが空になると、非同期的に割り当てが解除されます)。キューがもう必要ない場合にのみ、これを行う必要があります。を呼び出した場合dispatch_release()(つまり、ARC を使用していない場合)、将来のキュー変数の使用は信頼できません (もちろん、dispatch_release()が と一緒に実行された場合を除きdispatch_retain()ます)。最終的に解放したら、queue 変数を nil に設定して、使用したくならないようにすることもできます。dispatch_release()率直に言って、あなたが呼び出し可能な start メソッドを実行したこと、dispatch_suspend()およびキューを既に解放していることを考えると、重大な例外なく実行したことを考えると、私は驚いてdispatch_resume()います (たまたま、元のコード ブロックがそのキューはまだ完了していません)。

要するに、サスペンド キューを追跡し始める前に (これを知的好奇心から追跡しているのか、それとも解決しようとしている説得力のあるビジネス上の問題があったのかはわかりません)、質問を明確にすることをお勧めします。ビジネス ロジックと関連コード。

于 2012-05-16T21:21:58.213 に答える