Jak rozpocząć dwa wątki „dokładnie” w tym samym czasie


92

Wątki powinny rozpoczynać się w tym samym ułamku sekundy. Rozumiem, że jeśli to zrobisz thread1.start(), kolejne uruchomienie zajmie kilka milisekund thread2.start().

Czy to w ogóle możliwe czy niemożliwe?


2
Ułamek sekundy to bardzo długi czas przy prędkościach GHz.
Nikolai Fetissov

32
Nie ma potrzeby tak intensywnego głosowania przeciw. Nie każdy rozumie niedeterminizm związany z wątkami i wszyscy musimy od czegoś zacząć.
Michael Petrotta

7
Nie rozumiem głosów przeciw. Synchronizacja między wątkami jest bardzo powszechną koniecznością. Tak, w Javie nie można ich wykonywać dokładnie równolegle (co przy innych platformach może być bardzo ważnym wymaganiem), ale od czasu do czasu bardzo często trzeba zsynchronizować ich działanie. Dlatego jdk ma do tego klasy. Być może sformułowanie nie było dokładne, ale do diabła, gdyby to wiedział, nie
zadałby

cóż, chyba rozumiem całą twoją złość. To było pytanie zadane mi w wywiadzie… prawdopodobnie była to sztuczka. P: Ale po prostu się pomyliłem i chciałem to potwierdzić. dlatego zapytałem, czy to w ogóle możliwe.
figaro

2
@javaguy - to nie jest "podstępne" pytanie. Raczej pytanie wybrane, aby zobaczyć, jak dobrze rozumiesz ogólnie programowanie wielowątkowe ... a także w przypadku Javy.
Stephen C

Odpowiedzi:


136

Aby rozpocząć wątki dokładnie w tym samym czasie (przynajmniej tak dobrze, jak to możliwe), możesz użyć CyclicBarrier :

// We want to start just 2 threads at the same time, but let's control that 
// timing from the main thread. That's why we have 3 "parties" instead of 2.
final CyclicBarrier gate = new CyclicBarrier(3);

Thread t1 = new Thread(){
    public void run(){
        gate.await();
        //do stuff    
    }};
Thread t2 = new Thread(){
    public void run(){
        gate.await();
        //do stuff    
    }};

t1.start();
t2.start();

// At this point, t1 and t2 are blocking on the gate. 
// Since we gave "3" as the argument, gate is not opened yet.
// Now if we block on the gate from the main thread, it will open
// and all threads will start to do stuff!

gate.await();
System.out.println("all threads started");

To nie musi być a CyclicBarrier, możesz też użyć a, CountDownLatcha nawet zamka.

Wciąż nie można zagwarantować, że zostaną uruchomione dokładnie w tym samym czasie na standardowych maszynach JVM, ale można podejść całkiem blisko. Podejście dość blisko jest nadal przydatne, na przykład podczas testów wydajności. Na przykład, jeśli próbujesz zmierzyć przepustowość struktury danych z różną liczbą trafiających w nią wątków, chcesz użyć tego rodzaju konstrukcji, aby uzyskać jak najdokładniejszy wynik.

Na innych platformach, nici zaczynające dokładnie może być bardzo ważny wymóg btw.


5
+1 miłe myślenie ;-) Chociaż oczywiście nie powoduje to, że wątki zaczynają się dokładnie w tym samym czasie w czasie rzeczywistym (przynajmniej nie na chipie jednordzeniowym i nie gwarantowane nawet na chipie wielordzeniowym), ale Nie mogę wymyślić żadnego sposobu, aby zrobić coś lepszego.
David Z

Czy to podłączy uchwyty oczekiwania specyficzne dla środowiska?
ChaosPandion

@ChaosPandion: Chcesz się rozwinąć?
Santa

@Santa - Na przykład API Win32 oferuje różne prymitywy. Jednym z przydatnych typów jest zdarzenie resetowania ręcznego zwracane podczas połączenia CreateEvent. msdn.microsoft.com/en-us/library/ms686364%28VS.85%29.aspx
ChaosPandion,

1
@Zwei - Cokolwiek to jest, to jest odpowiedź, którą napisałbym, gdybym był guru Java.
ChaosPandion

15

Nie jest to możliwe, przynajmniej na komputerze jednordzeniowym. Ale dlaczego tego chcesz? Nawet jeśli byłbyś w stanie uruchomić dwa wątki dokładnie w tej samej sekundzie, będą one postępować inaczej, ponieważ planowanie nie jest pod Twoją kontrolą.

Edycja: (w odpowiedzi na niektóre komentarze) Jest to całkowicie ważny wymóg synchronizacji stanu lub postępu wielu wątków i CyclicBarrierjest świetnym narzędziem. Odpowiedziałem na pytanie, czy możliwe jest rozpoczęcie wielu wątków dokładnie w tym samym czasie . CyclicBarrierzagwarantuje, że wątki będą kontynuowane, gdy znajdą się dokładnie w pożądanym stanie, ale nie gwarantuje, że zostaną uruchomione lub wznowione dokładnie w tym samym czasie, chociaż może być dość blisko. W pytaniu nie ma wzmianki o potrzebach synchronizacji.


1
Możesz być jednak cholernie blisko. Wszystko zależy od stosowanych technik synchronizacji i oczywiście posiadania więcej niż 1 procesora lub rdzenia.
ChaosPandion

Chodzi o to, że jest to błędne podejście i nie powinno być konieczne w żadnym nie trudnym środowisku czasu rzeczywistego, a nie że jest „całkiem blisko”.
Nikolai Fetissov

1
Jest to również niemożliwe, ponieważ planowanie wątków Java nie zapewnia dokładności co do milisekund.
Stephen C

1
@Zwei - prawdopodobnie możesz dostać się „cholernie blisko” przez większość czasu na bezczynnej maszynie. Ale jeśli potrzebujesz tego przez cały czas, musisz programować w języku takim jak C na maszynie z twardą obsługą czasu rzeczywistego w systemie operacyjnym. Weź również pod uwagę, że JVM może uruchamiać pełny GC w „ułamku sekundy”, w którym chcesz rozpocząć wątki.
Stephen C

1
Jest to całkowicie poprawny problem z synchronizacją. Wątki muszą zainicjować niektóre dane, upewnij się, że nikt nie trafia do krytycznego regionu bez odpowiedniej weryfikacji. A fakt, że CyclicBerrier jest zawarty w pakiecie współbieżnym javas, oznacza, że ​​jest to ważny problem.
Denis Tulskiy

14

Możesz do tego użyć CountDownLatch. Poniżej próbkę. Chociaż t1 i t2 są uruchomione, te wątki czekają, aż główny wątek odliczy zatrzask. Liczba wymaganych odliczeń jest podana w konstruktorze. Zatrzask odliczania może być również używany do oczekiwania na zakończenie wykonywania wątków, aby wątek główny mógł przejść dalej (przypadek odwrotny). Ta klasa została uwzględniona od wersji Java 1.5.

import java.util.concurrent.CountDownLatch;


public class ThreadExample
{
    public static void main(String[] args) 
    {
        CountDownLatch latch = new CountDownLatch(1);
        MyThread t1 = new MyThread(latch);
        MyThread t2 = new MyThread(latch);
        new Thread(t1).start();
        new Thread(t2).start();
        //Do whatever you want
        latch.countDown();          //This will inform all the threads to start
        //Continue to do whatever
    }
}

class MyThread implements Runnable
{
    CountDownLatch latch;
    public MyThread(CountDownLatch latch) 
    {
        this.latch = latch;
    }
    @Override
    public void run() 
    {
        try 
        {
            latch.await();          //The thread keeps waiting till it is informed
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //Do the actual thing
    }
}

Ta odpowiedź mogłaby skorzystać na mniej standardowym przykładzie kodu.
user2418306

6
  1. Jak rozumiem, JVM głównie deleguje te rzeczy do systemu operacyjnego. Więc odpowiedź będzie zależna od systemu operacyjnego.
  2. Jest to oczywiście niemożliwe na komputerach jednoprocesorowych.
  3. Jest to bardziej skomplikowane w przypadku maszyny wieloprocesorowej. Zgodnie z teorią względności jednoczesności „nie można powiedzieć w sensie absolutnym, czy dwa zdarzenia zachodzą w tym samym czasie, jeśli są one rozdzielone w przestrzeni”. Bez względu na to, jak blisko są twoje procesory, są one rozdzielone przestrzenią.
    1. Jeśli możesz zaakceptować względną jednoczesność, prawdopodobnie łatwiej będzie po prostu zasymulować ją za pomocą technik omówionych w innych odpowiedziach.

1
… A nawet jeśli założymy jednoczesny start, każdy wątek ma swoją własną oś czasu, wszystkie z nich są współbieżne , ale niekoniecznie równoległe, więc cokolwiek ktoś przyjmie na temat jednoczesności wątków, może (będzie) nieważne w następnej nanosekundzie (niezależnie oś czasu)…
Holger
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.