c#编码技巧(四):多线程数据显示在UI-解决UI卡死问题-winform

先拖1个button,2个label,1个richTextBox到winform界面上,如图

 

代码如下,具体过程看备注

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace TaskWinform
{
    /// <summary>
    /// 本代码演示了:winform创建两个线程,线程完成后结果更新显示到UI上,同时UI不卡死。
    /// 本代码主旨为了解决线程同步和UI卡死问题
    /// </summary>
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        //记录线程的运行时间信息等,最后打印出来,为了验证线程是不是同时启动
        private List<string> mulLog = new List<string>();
        private List<string> addLog = new List<string>();

        //按键按下去后
        private void button1_Click(object sender, EventArgs e)
        {
            double[] arr = new double[2];

            //创建一个当前线程的任务计划
            var scheduler = TaskScheduler.FromCurrentSynchronizationContext();

            //异步线程
            Task.Run(() =>
            {
                //启动两个线程,并记录结果
                var m = Multiply();
                var a = Add();
                arr = new double[2] { m.Result, a.Result };

            }).ContinueWith(x =>
            {   //任务完成时,异步执行与scheduler相关的延续任务 ,x => 所指向的就是延续任务          
                //记录结果后,把结果打印出来,这里采用了委托异步调用,为了防止richTextBox打印大量log时,ui卡住问题
                Action act = delegate ()
                {
                    label1.Text = arr[0].ToString();
                    label2.Text = arr[1].ToString();
                    mulLog.ForEach(p => richTextBox1.AppendText(p + Environment.NewLine));
                    richTextBox1.AppendText("--------------------------------" + Environment.NewLine);
                    addLog.ForEach(p => richTextBox1.AppendText(p + Environment.NewLine));
                };
                this.BeginInvoke(act, null);

            }, scheduler);


        }

        //模拟一个耗时的线程,如获取数据库数据、计算数据等等
        private async Task<double> Multiply()
        {
            var task = Task.Run(() =>
            {
                double sum = 1, i = 1;
                long mi , preMi = 0;

                Stopwatch sw = new Stopwatch();     //创建一个Stopwatch,为了计时
                sw.Start();                         //计时开始

                //在设定的毫秒数内循环,模拟一个耗时的任务
                do
                {
                    sum *= i;
                    i++;
                    mi = sw.ElapsedMilliseconds;    //获取从计时开始的毫秒数        

                    //log记录task id,当前时间
                    string text = string.Format("{0}: task id = {1}, i={2}", DateTime.Now.ToString("yy-MM-dd hh:mm:ss.fff"), Task.CurrentId, i);
                    //每100ms记录一条不重复的log
                    //preMi:防止重复 , mi = preMi的log就不记录了
                    if (mi % 100 == 0 && mi != preMi)   
                    {
                        preMi = mi; 
                        mulLog.Add(text);
                    }
                       
                } while (mi < 2000);                 

                sw.Stop();
                return sum;

            });
            return await task;
        }

        //同上,模拟一个耗时的线程
        private async Task<double> Add()
        {
            var task = Task.Run(() =>
            {
                double sum = 0, j = 1;
                long mi, preMi = 0;

                Stopwatch sw = new Stopwatch();
                sw.Start();

                do
                {
                    sum += j;
                    j++;
                    mi = sw.ElapsedMilliseconds;
                    string text = string.Format("{0}: task id = {1}, j={2}", DateTime.Now.ToString("yy-MM-dd hh:mm:ss.fff"), Task.CurrentId, j);
                    if (mi % 100 == 0 && mi != preMi)
                    {
                        preMi = mi;
                        addLog.Add(text);
                    }
                } while (mi < 3000);

                sw.Stop();
                return sum;

            });
            return await task;
        }
    }
}

运行结果如下,看线程时间,两个线程是同时开启的,在线程运行过程中,ui不卡顿,界面可拖动。

 

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
消息总线是一种用于在应用程序中实现组件之间通信的模式。在WinForm应用程序中,可以使用C#的事件、委托和线程安全的队列来实现消息总线。下面是一个简单的示例代码,演示如何使用消息总线在多个界面和多个线程之间进行通信: ```csharp // 定义一个事件参数类,用于传递消息数据 public class MessageEventArgs : EventArgs { public string Message { get; set; } public MessageEventArgs(string message) { Message = message; } } // 定义一个消息总线类 public static class MessageBus { // 定义一个事件,用于发布消息 public static event EventHandler<MessageEventArgs> MessagePublished; // 定义一个线程安全的队列,用于存储消息 private static ConcurrentQueue<string> messageQueue = new ConcurrentQueue<string>(); // 定义一个定时器,用于定时检查消息队列并发布消息 private static System.Timers.Timer timer = new System.Timers.Timer(100); static MessageBus() { // 启动定时器 timer.Elapsed += Timer_Elapsed; timer.Start(); } private static void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { // 检查消息队列 while (messageQueue.TryDequeue(out string message)) { // 发布消息 MessagePublished?.Invoke(null, new MessageEventArgs(message)); } } // 定义一个静态方法,用于发布消息 public static void Publish(string message) { // 将消息添加到队列中 messageQueue.Enqueue(message); } } // 在多个界面中订阅消息总线的事件 public partial class Form1 : Form { public Form1() { InitializeComponent(); // 订阅消息总线的事件 MessageBus.MessagePublished += MessageBus_MessagePublished; } private void MessageBus_MessagePublished(object sender, MessageEventArgs e) { // 在UI线程中更新界面 if (InvokeRequired) { Invoke(new Action(() => MessageBus_MessagePublished(sender, e))); } else { // 处理消息 label1.Text = e.Message; } } // 在多个线程中发布消息 private void button1_Click(object sender, EventArgs e) { // 启动一个新线程 new Thread(() => { // 发布消息 MessageBus.Publish("Hello, World!"); }).Start(); } } ``` 在上面的示例代码中,我们定义了一个`MessageBus`类作为消息总线,其中包含一个事件`MessagePublished`和一个线程安全的队列`messageQueue`。在多个界面中订阅`MessagePublished`事件,并在事件处理程序中更新界面。在多个线程中,通过调用`MessageBus.Publish`方法来发布消息。定时器定时检查消息队列并发布消息。这样,我们就可以在多个界面和多个线程之间实现通信。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值