2012-01-25 21 views
8

Znalazłem kilka pytań dotyczących mojego problemu, ale nadal nie mogłem tego zrobić samodzielnie, więc postaram się zapytać tutaj. Wkleję ten kod, więc myślę, że będzie łatwiej wytłumaczyć.Przekazywanie danych między wątkami w C#

public partial class Form1 : Form 
{ 
    public Form1() 
    { 
     InitializeComponent(); 
     Thread thread = new Thread(new ThreadStart(StartCalculation)); 
     thread.Start(); 
    } 

    private void Form1_Load(object sender, EventArgs e) 
    { 

    } 


    public void StartCalculation() 
    { 
     List<int> numbers = new List<int>(); 
     for (int i = 0; i <= 100; i++) 
     { 
      numbers.Add(i); 
      string textForLabel = i.ToString(); 
      label.SafeInvoke(d => d.Text = textForLabel); 
     } 

    } 
} 
  • ja jak mieć dostęp ze sposobem StartCalculation który rozpoczął się w inny gwint. Chciałbym uzyskać dostęp do tej listy int z Form1 (10 elementów po 10 sekundach, 20 elementów po 20 sekundach i tak). Czy to jest możliwe?
  • Czy tworzenie listy w Form1(), a następnie zmiana w StartCalculation będą możliwe? Dzięki za odpowiedzi :)

Edited dla Groo-/-

public partial class Form1 : Form 
{ 

List<int> list = new List<int>(); // list of int values from game's memory 

public Form1() 
{ 
    InitializeComponent(); 
    Thread thread = new Thread(new ThreadStart(refreshMemory)); 
    thread.Start(); 
    Thread thread2 = new Thread(new ThreadStart(checkMemory)); 
    thread2.Start(); 
} 

private void Form1_Load(object sender, EventArgs e) 
{ 
} 

public void refreshMemory() 
{   
    while (true) 
    { 
    // ... refresh game's memory and then, refresh list // 
    Thread.Sleep(100); 
    } 
} 

public void checkMemory() 
{ 

    while (true) 
    { 
    // eg. if (list[0] == 5) {game:: move_right()}// 
    Thread.Sleep(100); 
    } 

} 

} 

Robię gry bot. Chcę, żeby odczytywała pamięć gry w innym wątku (zmieniając listę pamięci), a następnie, przy pomocy kilku innych metod (w różnych wątkach), chciałbym przeczytać z tej listy i wykonać czynności w grze w zależności od wartości pamięci. Działa (lub po prostu wydaje się być), ale jeśli powiesz, że to może być niebezpieczne, chciałbym, aby było to bezpieczne.

Mam nadzieję, że nie zrobiłem z siebie głupka, wklejając go tutaj.

+6

Oto książka: [Threading in C#, Joseph Albahari] (http://www.albahari.com/threading/) – Sjoerd

+3

Cóż, dziękuję! Dzięki tej książce jest już rozwiązany :) – Patryk

+0

* "10 elementów po 10 sekundach, 20 elementów po 20 sekundach" * - dlaczego te opóźnienia? Co się stanie, jeśli wątek tła tworzy elementy szybciej? Czy może to działa jako kolejka producenta/konsumenta, ale z progiem min. 10 przedmiotów? – Groo

Odpowiedz

19

trzeba jakąś formę mechanizm synchronizacji do modyfikowania obiektów między wieloma wątkami. Jeśli nie używasz specjalistycznego bezpiecznego zbierania wątków (są one dostępne w .NET 4), musisz zablokować za pomocą monitora.

Zazwyczaj bardziej odpowiedni typ kolekcji na wzór producent/konsumentów jest Queue (zbiór FIFO), zamiast List:

Plain kolejki z wyraźną blokowania

private readonly object _lock = new object(); 
private readonly Queue<Item> _queue = new Queue<Item>(); 
private readonly AutoResetEvent _signal = new AutoResetEvent(); 

void ProducerThread() 
{ 
    while (ShouldRun) 
    { 
     Item item = GetNextItem(); 

     // you need to make sure only 
     // one thread can access the list 
     // at a time 
     lock (_lock) 
     { 
      _queue.Enqueue(item); 
     } 

     // notify the waiting thread 
     _signal.Set(); 
    } 

} 

I w wątku konsumenta musisz pobrać przedmiot i przetworzyć go:

void ConsumerThread() 
{ 
    while (ShouldRun) 
    { 
     // wait to be notified 
     _signal.Wait(); 

     Item item = null; 

     do 
     { 
      item = null; 

      // fetch the item, 
      // but only lock shortly 
      lock (_lock) 
      { 
       if (_queue.Count > 0) 
        item = _queue.Dequeue(item); 
      } 

      if (item != null) 
      { 
       // do stuff 
      }    
     } 
     while (item != null); // loop until there are items to collect 
    } 
} 

Począwszy od .NET 4, jest ConcurrentQueue<T> zbiór, nitki bezpieczny FIFO, co eliminuje potrzebę, aby zablokować go podczas uzyskiwania dostępu i upraszcza kod:

ConcurrentQueue

private readonly ConcurrentQueue<Item> _queue = new ConcurrentQueue<Item>(); 

void ProducerThread() 
{ 
    while (ShouldRun) 
    { 
     Item item = GetNextItem(); 
     _queue.Enqueue(item); 
     _signal.Set(); 
    } 

} 

void ConsumerThread() 
{ 
    while (ShouldRun) 
    { 
     _signal.Wait(); 

     Item item = null; 
     while (_queue.TryDequeue(out item)) 
     { 
      // do stuff 
     } 
    } 
} 

Wreszcie, jeśli chcesz tylko, aby twój wątek konsumenta okresowo pobierał elementy w kawałkach, zmieniłbyś to na:

Współbieżność z progiem (10 sek. lub 10 sztuk)

private readonly ConcurrentQueue<Item> _queue = new ConcurrentQueue<Item>(); 

void ProducerThread() 
{ 
    while (ShouldRun) 
    { 
     Item item = GetNextItem(); 
     _queue.Enqueue(item); 

     // more than 10 items? panic! 
     // notify consumer immediately 

     if (_queue.Count >= 10) 
      _signal.Set(); 
    } 

} 

void ConsumerThread() 
{ 
    while (ShouldRun) 
    { 
     // wait for a signal, OR until 
     // 10 seconds elapses 
     _signal.Wait(TimeSpan.FromSeconds(10)); 

     Item item = null; 
     while (_queue.TryDequeue(out item)) 
     { 
      // do stuff 
     } 
    } 
} 

Ten wzór jest tak przydatny, że miło jest abstrakcyjne go do ogólnej klasy, która deleguje produkujących i zużywających do kodu zewnętrznego. Byłoby dobrym ćwiczeniem, aby było ogólne.

Potrzebna będzie również metoda Stop, która prawdopodobnie ustawi flagę volatile bool wskazującą, że nadszedł czas na zatrzymanie, a następnie ustawia sygnał, aby wznowił działanie konsumenta i pozwolił mu się zakończyć. Zostawię to dla ciebie jako ćwiczenie.

+0

Jestem tylko czytania z kilku wątków z jednego obiektu i jeśli chcę zmodyfikować, mam zamiar pozwolić 1 wątku modyfikować 1 obiekt. Czy w takim razie jest ok bez tych rzeczy? – Patryk

+1

Jeśli inny wątek pisze do obiektu, który nie jest bezpieczny dla wątków, ** musisz zablokować zarówno czytanie, jak i zapisywanie **. Pod względem wydajności, jeśli czytasz wiele wątków i piszesz tylko jeden, możesz rozważyć użycie [ReaderWriterLockSlim] (http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim.aspx) , który został zaprojektowany, aby umożliwić większą przepustowość wielu współbieżnym czytelnikom. Ale dla małej liczby czytelników może być wolniejsza od zwykłego zamka i nieco bardziej skomplikowana w implementacji (więc nie brałbym tego pod uwagę w tej chwili). – Groo

+1

Tak, tak, potrzebujesz obiektu zabezpieczonego wątkiem, jeśli chcesz go zmodyfikować z innego wątku. Jeśli jest to lista, najprawdopodobniej potrzebujesz "ConcurrentQueue". Jeśli nie, opublikuj kilka dodatkowych szczegółów. – Groo

0

w tym przypadku użyć

label.Invoke(..., textForLabel) 
Powiązane problemy