c#线程间数据传递

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8999373/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 05:30:55  来源:igfitidea点击:

Passing data between threads in c#

c#multithreading

提问by Patryk

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 :)
  • 我想从不同线程中启动的方法 StartCalculation 获得访问权限。我想从 Form1 访问该 int 列表(10 秒后 10 个元素,20 秒后 20 个元素等等)。那可能吗?
  • 是否可以在 Form1() 中创建列表,然后在 StartCalculation 中更改它?感谢您的回答:)

Edited for Groo- / -

为 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.

希望我没有把它贴在这里把自己弄糊涂了。

采纳答案by Groo

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.

您需要某种形式的同步机制来修改多个线程之间的对象。如果您不使用专门的线程安全集合(这些在 .NET 4 中可用),则需要使用监视器进行锁定。

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

通常,更适合生产者/消费者模式的集合类型是 a Queue(FIFO 集合),而不是 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:

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

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)

具有阈值的 ConcurrentQueue(10 秒或 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
        }
    }
}

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 Stopmethod which will probably set a volatile boolflag 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.

您还需要一个Stop方法,该方法可能会设置一个volatile bool标志,指示该停止了,然后设置信号以取消暂停消费者并允许它结束。我将把它留给你作为练习。

回答by Bonshington

in this case use

在这种情况下使用

label.Invoke(..., textForLabel)