Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing data between threads in c#

I've found few questions concerning my problem but still, I couldn't hande with this on my own so I'll try to ask in here. I'll paste the code so I think it will be easier to explain.

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

    }  
}
  • I would like to have an acces from method StartCalculation which started in different thread. I'd like to access that int list from Form1 (10 elements after 10 secs, 20 elements after 20 secs and so). Is that possible?
  • Would creating list in Form1() and then changing it in StartCalculation be possible? Thanks for the answers :)

Edited for 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);
    }

}  

}

I'm making game bot. I want it to read game's memory in a different thread (changing memory list) and then, with few other methods (in different threads) I'd like to read from that list and do game actions depending on the memory values. It works (or just seem to be) but if You say it might be unsafe, I'd like to make it safe.

Hope I haven't made fool out of myself by pasting it here.

like image 214
Patryk Avatar asked Jan 25 '12 07:01

Patryk


People also ask

How do you pass information between threads?

If you want synchronous communication between a main thread and a processing thread, you can use a SynchronousQueue. The idea is that the main thread passes data to the processing thread by calling put() , and the processing thread calls take() . Both are blocking operations.

What is shared between threads C?

Every thread shares common addressing space, so a local variable inside a thread can be seen by another thread using pointers.

Is C good for multithreading?

Can we write multithreading programs in C? Unlike Java, multithreading is not supported by the language standard. POSIX Threads (or Pthreads) is a POSIX standard for threads.

Can two threads access the same data at the same time?

A data race is a state, in which at least two threads access shared data at the same time, and at least one of the threads is a writer. A critical section is a section of the code, which not more than one thread should access at any point in time.


1 Answers

You need some form of a synchronization mechanism to modify objects between multiple threads. If you don't use a specialized thread safe collection (these are available in .NET 4), you need to lock using a monitor.

Usually, a more appropriate collection type for the producer/consumer pattern is a Queue (a FIFO collection), instead of a List:

Plain Queue with explicit locking

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

}

And in the consumer thread, you need to fetch the item and process it:

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

Starting with .NET 4, there is a ConcurrentQueue<T> collection, a thread-safe FIFO, which removes the need to lock while accessing it and simplifies the code:

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

Finally, if you only wish that your consumer thread gets items in chunks periodically, you would change this to:

ConcurrentQueue with threshold (10 sec. or 10 items)

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

This pattern is so useful that it's nice to abstract it into a generic class which delegates producing and consuming to external code. It would be a good exercise to to make it generic.

You will also need a Stop method which will probably set a volatile bool flag indicating that it's time to stop, and then set the signal to unpause the consumer and allow it to end. I will leave this to you as an exercise.

like image 160
Groo Avatar answered Oct 14 '22 18:10

Groo