2012-01-25 17 views
8

He encontrado algunas preguntas sobre mi problema, pero aun así, no pude con esto por mi cuenta, así que trataré de preguntar aquí. Pegaré el código así que creo que será más fácil de explicar.Pasar datos entre hilos en 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); 
     } 

    } 
} 
  • me gustaría tener un acceso desde StartCalculation método que comenzaron en hilo diferente. Me gustaría acceder a esa lista int de Form1 (10 elementos después de 10 segundos, 20 elementos después de 20 segundos, etc.). ¿Es eso posible?
  • ¿Sería posible crear una lista en Form1() y luego cambiarla en StartCalculation? Gracias por las respuestas :)

Editado por 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); 
    } 

} 

} 

estoy haciendo bot juego. Quiero que lea la memoria del juego en un hilo diferente (cambiar la lista de memoria) y luego, con algunos otros métodos (en diferentes hilos) me gustaría leer de esa lista y realizar acciones de juego dependiendo de los valores de memoria. Funciona (o parece que lo es) pero si dices que podría ser inseguro, me gustaría que sea seguro.

Espero no haber hecho el tonto de mi mismo al pegarlo aquí.

+6

Aquí hay un libro: [Enhebrado en C#, Joseph Albahari] (http://www.albahari.com/threading/) – Sjoerd

+3

¡Bien, gracias! Gracias a ese libro, ahora está resuelto :) – Patryk

+0

* "10 elementos después de 10 segundos, 20 elementos después de 20 segundos" *: ¿por qué estas demoras? ¿Qué pasa si el hilo de fondo produce elementos a un ritmo más rápido? O debería funcionar como una cola de productor/consumidor, pero con un umbral de mín. 10 artículos? – Groo

Respuesta

19

Es necesario alguna forma de un mecanismo de sincronización para modificar objetos entre varios subprocesos. Si no utiliza una colección segura para hilos especializados (están disponibles en .NET 4), debe bloquear usando un monitor.

Por lo general, un tipo de colección más apropiado para el patrón de productor/consumidor es un (una colección FIFO) Queue, en lugar de un List:

Llanura cola con bloqueo explícita

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

} 

Y en el hilo del consumidor, debe buscar el elemento y procesarlo:

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

A partir de .NET 4, hay una colección ConcurrentQueue<T>, un FIFO flujos seguros, lo que elimina la necesidad de bloquear mientras se accede a ella y simplifica el código:

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

Por último, si solo desea que su hilo de consumidor obtenga los artículos en porciones periódicamente, lo cambiaría a:

ConcurrenteQueue con umbral (10 seg. o 10 artículos)

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

Este patrón es tan útil que es bueno para abstraer en una clase genérica que los delegados a la producción y consumo de código externo. Sería un buen ejercicio hacerlo genérico.

También necesitará un método Stop que probablemente establecerá un indicador volatile bool que indica que es hora de detenerse, y luego establecer la señal para reanudar el consumo y permitir que termine. Te dejaré esto como un ejercicio.

+0

Solo estoy leyendo con pocos hilos de un objeto y si quiero modificarlo, estoy planeando permitir que 1 hilo modifique 1 objeto. ¿Está bien sin tu cosa, entonces? – Patryk

+1

Si un hilo diferente está escribiendo en un objeto que no es seguro para subprocesos, entonces ** necesita bloquear tanto la lectura como la escritura **. En cuanto a rendimiento, si tiene muchos hilos de lectura y solo uno escribe, puede considerar usar un [ReaderWriterLockSlim] (http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim.aspx) , que está diseñado para permitir un mayor rendimiento para muchos lectores concurrentes. Pero para un número bajo de lectores, puede ser aún más lento que un bloqueo normal y algo más complicado de implementar (por lo que no lo consideraría en este momento). – Groo

+1

Entonces, sí, necesita un objeto seguro para subprocesos si desea modificarlo desde un subproceso diferente. Si se trata de una lista, lo más probable es que necesite un 'ConcurrentQueue'. Si no es así, publique algunos detalles adicionales. – Groo

0

en este caso de uso

label.Invoke(..., textForLabel) 
Cuestiones relacionadas