C#从不同的线程

时间:2018-04-23 12:45:22

标签: c# multithreading winforms listview thread-safety

我一直在查看https://docs.microsoft.com/en-us/dotnet/framework/winforms/controls/how-to-make-thread-safe-calls-to-windows-forms-controls和其他Stack Overflow问题很长一段时间,以便弄清楚如何使用线程安全方法从不同的线程访问ListView控件。

以下是我想在程序中实现并行任务的方法: 我将四种不同的方法并行调用:

Parallel.Invoke(ProcessLow, ProcessMed, ProcessHigh, ProcessSprint);

每个方法使用for循环搜索相同的collectiondata[i].Knots)并在collection内查找不同的值范围,然后如果其中一个方法找到合适的值在它所寻找的范围内的值会将时间和节点(data[i].Timedata[i].Knots)添加到其各自的ListView(方法写入lstvLowlstvMed ,分别为lstvHighlstvSprint。目前它只是抛出非线程安全代码的异常。如果我有不同的线程只是读取同一个集合,它也会破坏吗? (如果是这样,我该如何解决这个问题?)

tldr:并行处理对我来说是新的,如何对Windows窗体控件进行线程安全调用。
如果可以的话,请指出除了msdn之外的平行任务的一些好的阅读方向。

编辑:这是winforms

2 个答案:

答案 0 :(得分:1)

进行线程安全呼叫。使用Control.Invoke();方法。假设你有ListView mylistView;的实例,你可以写:

object result =  mylistView.Invoke(new Action(() => mylistView.DoSomething()));

答案 1 :(得分:0)

据我了解,您是多任务处理的新手。我希望我的案例研究会对你有所帮助。使用下一个控件创建一个窗体:

  • startButtonstopButton为按钮
  • minTextEditmaxTextEdit为TextEdit
  • listListView作为ListView

对于startButton和stopButton,请使用适当的方法:startButton_ClickstopButton_Click

完整下面的代码:

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Thread0
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }

        private static readonly Random _random = new Random();
        private List<int> lst = new List<int>();
        private bool isRun = false;

        private void startButton_Click(object sender, EventArgs e)
        {
            isRun = true;
            stopButton.Enabled = true;
            startButton.Enabled = false;
            var tskMain = Task.Run(() =>
            {
                for (int i = 0; i < 8; i++)
                {
                    var tsk1 = Task.Run(() =>
                    {
                        while (true)
                        {
                            int max = 0;
                            int min = Int32.MaxValue;
                            lock (lst)
                            {
                                int num = _random.Next(1, 1000000);
                                lst.Add(num);
                                foreach (var x in lst)
                                {
                                    if (x > max) max = x;
                                    if (min > x) min = x;
                                }
                                listListView.BeginInvoke(new Action(() => listListView.Items.Insert(0, num.ToString())));
                            }
                            maxTextBox.BeginInvoke(new Action(() => maxTextBox.Text = max.ToString()));
                            minTextBox.BeginInvoke(new Action(() => minTextBox.Text = min.ToString()));

                            if (!isRun) break;

                            Thread.Sleep(100);
                        }
                    });
                }
            });
        }

        private void stopButton_Click(object sender, EventArgs e)
        {
            isRun = false;
            stopButton.Enabled = false;
            startButton.Enabled = true;
        }
    }
}

当您单击“开始”按钮时,流创建并运行tskMain线程,这将创建另外8个线程。在每个值中,将一个整数添加到值lst列表中,并搜索最大值和最小值。新号码也会添加到listListView,以及相应minTextEditmaxTextEdit中的最大值和最小值。

对于工作来说,使用lambda表达式很方便。

lock(lst) ...块可以处理值列表。每次一个动作,因此没有例外。

BeginInvoke方法允许您从主要表单流中的表单元素的线程调用方法。 Action用于将lambda表达式“转换”为委托方法。

在每个线程中,检查变量IsR​​un,如果其值变为false,则线程执行停止。

好吧,为确保一切都不会很快使用.Sleep