2010-10-07 7 views

Odpowiedz

0

Proponuję zawinięcie wywołania metody klasy w twojej własnej metodzie i ustawienie wartości boolowskiej po jej zakończeniu. Dla np

BOOL isThreadRunning = NO; 
- (void)beginThread { 
    isThreadRunning = YES; 

    [self performSelectorInBackground:@selector(backgroundThread) withObject:nil]; 
} 
- (void)backgroundThread { 
    [myClass doLongTask]; 

    // Done! 
    isThreadRunning = NO; 
} 
- (void)waitForThread { 
    if (! isThreadRunning) { 
     // Thread completed 
     [self doSomething]; 
    } 
} 

Jak chcesz obsłużyć oczekujące należy do Ciebie: Może odpytywania z [NSThread sleepForTimeInterval: 1] lub podobne, lub wysyłając wiadomość SELF każdej pętli run.

+0

Zrobiłem to i działa, ale również uniemożliwia wyświetlanie ekranu HUD. –

+0

Możesz użyć narzędzia NSTimer do odpytywania i aktualizacji aktualizacji interfejsu użytkownika. Wszystko zależy od tego, czego potrzebujesz. Dla prostego zadania, a następnie zaktualizować odpowiedź Derek byłoby bardziej i mniej kłopotliwe. – v01d

+0

Dla silnika gry lub czegoś więcej w czasie rzeczywistym zegar może i tak już być na miejscu. Wtedy wysłanie wątku i zapomnienie o nim może być tym, czego potrzebujesz. – v01d

6

Moja pierwsza skłonność to nie robienie tego, co sugerujesz. Technika, której użyłem wcześniej, to nadanie wątkowi selektora do metody w obiekcie początkowym (który znajduje się w głównym wątku). Po uruchomieniu drugiego wątku główny wątek nadal wykonuje, ale wyświetla na wyświetlaczu jakiś zajęty wskaźnik. Pozwala to na kontynuację interakcji użytkownika, jeśli jest to wymagane.

Po zakończeniu drugiego wątku, tuż przed jego zamknięciem, wywoływana jest selektor głównego wątku. Metoda, którą odwołuje się selektor, usuwa wtedy wskaźnik zajętości z ekranu i informuje główny wątek do aktualizacji, zbierając dane, które wygenerował drugi wątek.

Z powodzeniem używam tej aplikacji, która uzyskuje dostęp do usługi sieciowej (w drugim wątku), a następnie aktualizuje ekran po zwrocie danych bez ich blokowania. To sprawia, że ​​użytkownik jest znacznie milszy.

+1

To jest ładny system do aktualizacji interfejsu; mogą być bólem. – v01d

8

Użyć NSOperationQueue, tak
(z pamięci więc wybaczyć jakieś drobne błędy - dostaniesz podstawową ideę):


// ivars 
NSOperationQueue *opQueue = [[NSOperationQueue alloc] init]; 
// count can be anything you like 
[opQueue setMaxConcurrentOperationCount:5]; 

- (void)main 
{ 
    [self doStuffInOperations]; 
} 

// method 
- (void)doStuffInOperations 
{ 
    // do parallel task A 
    [opQueue addOperation:[[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSomething:) object:@"a"] autorelease]]; 

    // do parallel task B 
    [opQueue addOperation:[[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSomething:) object:@"b"] autorelease]]; 

    // do parallel task C 
    [opQueue addOperation:[[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSomething:) object:@"c"] autorelease]]; 


    [opQueue waitUntilAllOperationsHaveFinished]; 

    // now, do stuff that requires A, B, and C to be finished, and they should be finished much faster because they are in parallel. 
} 

- (void)doSomething:(id)arg 
{ 
    // do whatever you want with the arg here 
    // (which is in the background, 
    // because all NSOperations added to NSOperationQueues are.) 
} 


+1

Wiem, że nie zadałem tego pytania, ale to było to, czego szukałem z moją aplikacją, ponieważ miałem warunki wyścigowe i wiedziałem, że to dlatego, że mogłem kliknąć szybciej, a kod mógł się ukończyć, więc stan częściowego obciążenia na widokach . To rozwiązało mój problem przez długi czas. Więc dziękuję, proszę pana, mam wrażenie, że powinno to być oznaczone jako prawdziwa odpowiedź, ponieważ nie używa pracy i zawiera bezpieczne kodowanie wątków z biblioteką ios. – Rob

+0

Po drugie, co powiedział Rob, to jest dokładnie to, czego szukałem! – AdamM

+1

@AdamM - sprawdź drugą odpowiedź, którą dzisiaj opublikowałem. Jest to o wiele łatwiejszy sposób, a GCD jest obsługiwany dla wszystkich systemów iOS 4 i nowszych. Istotną zaletą tego rozwiązania jest to, że nie dostaniesz awarii, gdy obiekty zostaną zwolnione spod Ciebie, ponieważ GCD automatycznie zachowuje i zwalnia wszystkie obiekty, które polecasz. – jpswain

35

Oto kolejny sposób to zrobić przy użyciu GCD:



- (void)main 
{ 
    [self doStuffInOperations]; 
} 

- (void)doStuffInGCD 
{ 
    dispatch_group_t d_group = dispatch_group_create(); 
    dispatch_queue_t bg_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 

    dispatch_group_async(d_group, bg_queue, ^{ 
     [self doSomething:@"a"]; 
    }); 

    dispatch_group_async(d_group, bg_queue, ^{ 
     [self doSomething:@"b"]; 
    }); 

    dispatch_group_async(d_group, bg_queue, ^{ 
     [self doSomething:@"c"]; 
    }); 


    // you can do this to synchronously wait on the current thread: 
    dispatch_group_wait(d_group, DISPATCH_TIME_FOREVER); 
    dispatch_release(d_group); 
    NSLog(@"All background tasks are done!!"); 


    // **** OR **** 

    // this if you just want something to happen after those are all done: 
    dispatch_group_notify(d_group, dispatch_get_main_queue(), ^{ 
     dispatch_release(d_group); 
     NSLog(@"All background tasks are done!!");   
    }); 
} 

- (void)doSomething:(id)arg 
{ 
    // do whatever you want with the arg here 
} 
+4

Pozdrawiam! Ta metoda jest jeszcze lepsza: – AdamM

+0

+1 dla właściwej odpowiedzi. – Abizern

+0

@node ninja Czy wybrałbyś odpowiedź na swoje pytanie? – jpswain

0

Moja technika jest sprawdzenie, czy zadanie można uruchomić (jeżeli wątek tła jest zakończone), a jeśli nie można uruchomić następnie używam GCD spróbować ponownie po upływie:

- (void)doSomething 
{ 
    if (/* is the other thread done yet? */) { 
    double delayInSeconds = 2.0; 
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 
     [self doSomething]; 
    }); 
    return; 
    } 

    // do something 
} 
1

Kilka technicznych sposobów synchronizacji wielu wątków, takich jak NSConditionLock (mutex-lock), NSCondition (semafor) .Jednak są one popularną wiedzą programistyczną dla innych języków (java ...) poza celem-c. Wolę przedstawić run loop (specjalne w Cocoa) w celu wdrożenia wątek dołączyć:

NSThread *A; //global 
A = [[NSThread alloc] initWithTarget:self selector:@selector(runA) object:nil]; //create thread A 
[A start]; 

- (void)runA  
{ 
  [NSThread detachNewThreadSelector:@selector(runB) toTarget:self withObject:nil]; //create thread B  
  while (1)  
  {  
    if ([[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) //join here, waiting for thread B  
    {  
      NSLog(@"thread B quit...");  
      break;  
    }  
  }  
} 

- (void)runB  
{  
  sleep(1);  
  [self performSelector:@selector(setData) onThread:A withObject:nil waitUntilDone:YES modes:@[NSDefaultRunLoopMode]];  
} 
3

W takich przypadkach zwykle za pomocą klasy NSCondition.

//this method executes in main thread/queue 
- (void)waitForJob 
{ 
    id __weak selfWeak = self; 
    NSCondition *waitHandle = [NSCondition new]; 
    [waitHandle lock]; 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ 
     [selfWeak doSomethingLongtime]; 
     [waitHandle signal]; 
    }); 
    //waiting for background thread finished 
    [waitHandle waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:60]]; 
} 
Powiązane problemy