C# 多线程处理List数据的示例代码

背景

在我做WinForm开发的过程中,经常会遇到耗时操作或阻塞操作。他们会引发软件的卡顿甚至假死,严重影响软件的使用。因此,这类耗时或阻塞的操作一般都会使用异步的方式去执行,不影响主线程(UI线程)与用户间的交互。但多个线程竞争读写同一个资源往往会造成意想不到的意外结果,UI界面也是一种资源,所以跨线程修改UI界面往往被加以限制。而在Winform中,跨线程修改UI界面同样是不被允许的。在子线程中修改界面控件时Visual Studio会报出如下错误:

解决思路

.Net提供了很多跨线程修改UI的方法,每种方法也有与之对应的工具类。我最常用的方法是ThreadPool+ delegate的方式完成跨线程对UI对的修改。

实例代码

1、定义委托,把内容写在控件里面

代码思路

将要处理的数据放到ConcurrentQueue中,然后开启多个线程去处理数据,处理完成后,再到队列中获取下一个待处理数据。

ConcurrentQueue 表示线程安全的先进先出 (FIFO) 集合,属于 System.Collections.Concurrent 命名空间下的一个数据结构

直接上代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

/// <summary>

/// 多线程处理数据(无返回值)

/// </summary>

/// <typeparam name="T">数据类型</typeparam>

/// <param name="list">待处理数据</param>

/// <param name="action">数据处理方法(有参数无返回值)</param>

/// <param name="count">处理线程数量</param>

/// <param name="waitFlag">是否等待执行结束</param>

static void RunTask<T>(List<T> list, Action<T> action, int threadCount = 5, bool waitFlag = true)

{

  ConcurrentQueue<T> queue = new ConcurrentQueue<T>(list);

  Task[] tasks = new Task[threadCount];

  for (int i = 0; i < threadCount; i++)

  {

    tasks[i] = Task.Run(() =>

     {

       while (queue.TryDequeue(out T t))

       {

         action(t);

       }

     });

  }

  if (waitFlag)

  {

    Task.WaitAll(tasks);

  }

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

/// <summary>

/// 多线程处理数据(返回处理后列表)

/// </summary>

/// <typeparam name="T">数据类型</typeparam>

/// <param name="list">待处理数据</param>

/// <param name="func">数据处理方法(有参数有返回值)</param>

/// <param name="threadCount">处理线程数量</param>

/// <returns>数据处理后结果</returns>

static List<T> RunTask<T>(List<T> list, Func<T, T> func, int threadCount = 5)

{

  var result = new List<T>();

  ConcurrentQueue<T> queue = new ConcurrentQueue<T>(list);

  Task<List<T>>[] tasks = new Task<List<T>>[threadCount];

  for (int i = 0; i < threadCount; i++)

  {

    tasks[i] = Task.Run<List<T>>(() =>

    {

      var rList = new List<T>();

      while (queue.TryDequeue(out T t))

      {

        rList.Add(func(t));

      }

      return rList;

    });

  }

  Task.WaitAll(tasks);

  for (int i = 0; i < threadCount; i++)

  {

    result.AddRange(tasks[i].Result);

  }

  return result;

}

调用方法

1

2

3

4

5

6

7

List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

//输出列表中的数据,且加上“action”字符前缀

RunTask<int>(list, d => { Console.WriteLine("action" + d); });

//对列表中数据都执行 “*2” 的操作

var result = RunTask<int>(list, d => { return d * 2; });

result.ForEach(d => Console.WriteLine(d));

最后的话

上面的代码只是简单的实现了对数据的处理,并没有考虑到对内存的使用限制,一般的项目中使用还是可以的。
最后我尝试了下,生成一个100M的列表,然后将其加载到ConcurrentQueue,监控程序内存占用,发现没有很大的内存占用变化。

2、定义方法,线程调用

1

2

3

4

5

6

7

8

9

10

11

private void button3_Click(object sender, EventArgs e)

    {

      ThreadPool.QueueUserWorkItem(new WaitCallback(dao), "test");

    }

 private void dao(object url)

    {

      IDbConnection conn = new SqlConnection(txtbigcrmui.Text);

      SetText("\r\n" + "start");

     .........................

      SetText("\r\n" + "end");

    }

3、更新ui控件

1

SetText("\r\n" + "start");

总结

1、使用delegate实现跨线程更新UI;

2、使用ThreadPool实现多线程执行方法,避免卡顿;

以上就是C#-WinForm跨线程修改UI界面的示例的详细内容,更多关于C#-WinForm跨线程的资料请关注脚本之家其它相关文章!

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值