2012-01-25 14 views
8

Я нашел несколько вопросов, касающихся моей проблемы, но все же, я не мог передать это самостоятельно, поэтому я попробую попросить здесь. Я вставлю код, поэтому я думаю, что будет легче объяснить.Передача данных между потоками в 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); 
     } 

    } 
} 
  • Я хотел бы иметь из-доступ метода StartCalculation, который начался в другом потоке. Я хотел бы получить доступ к этому списку int из Form1 (10 элементов через 10 секунд, 20 элементов через 20 секунд и т. Д.). Это возможно?
  • Возможно ли создание списка в Form1(), а затем его изменение в StartCalculation? Спасибо за ответы :)

Edited для 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); 
    } 

} 

} 

Я делает игру бота. Я хочу, чтобы он читал память игры в другом потоке (меняя список памяти), а затем, используя несколько других методов (в разных потоках), я хотел бы прочитать из этого списка и выполнять игровые действия в зависимости от значений памяти. Он работает (или просто кажется), но если вы скажете, что это может быть опасно, я бы хотел сделать это безопасным.

Надеюсь, я не сделал дурака из себя, вставив его здесь.

+6

Вот книга: [Threading in C#, Joseph Albahari] (http://www.albahari.com/threading/) – Sjoerd

+3

Хорошо, спасибо! Благодаря этой книге она решена сейчас :) – Patryk

+0

* «10 элементов через 10 секунд, 20 элементов через 20 секунд» * - почему эти задержки? Что, если фоновый поток производит элементы с большей скоростью? Или это должно работать как очередь производителей/потребителей, но с порогом мин. 10 предметов? – Groo

ответ

19

Вам потребуется некоторая форма механизма синхронизации для изменения объектов между несколькими потоками. Если вы не используете специализированную поточную безопасную коллекцию (они доступны в .NET 4), вам необходимо заблокировать ее с помощью монитора.

Обычно, более подходящий тип коллекции для шаблона производитель/потребитель является Queue (коллекция FIFO), вместо List:

Plain Queue с явным блокировки

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(); 
    } 

} 

И в потребительской нити, вам необходимо взять товар и обработать его:

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 
    } 
} 

Начиная с .NET 4, есть ConcurrentQueue<T> коллекции, потокобезопасный FIFO, который устраняет необходимость блокировки при доступе к его и упрощает код:

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 
     } 
    } 
} 

Наконец, если вы только хотите, чтобы ваша потребительская нить периодически получала предметы в кусках, вы бы изменили это на:

ConcurrentQueue with threshold (10 sec. или 10 штук)

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 
     } 
    } 
} 

Этот шаблон является настолько полезным, что это приятно абстрагировать в общий класс, который делегаты производства и потребления к внешнему коду. Было бы хорошим упражнением сделать это родовым.

Вам также понадобится метод Stop, который, вероятно, установит флаг volatile bool, указывающий, что пришло время остановиться, а затем установите сигнал, чтобы отключить пользователя и разрешить его завершить. Я оставлю это вам как упражнение.

+0

Я читаю только несколько потоков из одного объекта, и если я хочу изменить, я планирую разрешить 1 поток модифицировать 1 объект. Это нормально без ура? – Patryk

+1

Если другой поток пишет объект, который не является потокобезопасным, вам необходимо заблокировать чтение и запись **. По производительности, если у вас много чтения потоков и только одна запись, вы можете использовать [ReaderWriterLockSlim] (http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim.aspx) , который предназначен для обеспечения большей пропускной способности для многих параллельных считывателей. Но для небольшого числа читателей он все равно может быть медленнее обычного замка и немного сложнее реализовать (поэтому я бы не стал его рассматривать в это время). – Groo

+1

Итак, да, вам нужен потокобезопасный объект, если вы хотите изменить его из другого потока. Если это список, вам, скорее всего, потребуется «ConcurrentQueue». Если это не так, напишите дополнительные данные. – Groo

Смежные вопросы