winform多线程更新控件问题求助

weixin_49541915 2021-02-18 08:46:31
小弟学习多线程,准备写一个简单的窗体,往listview中写入数据,目前功能基本实现,就是在写入时,窗体假死,请大佬帮忙,谢谢。

要求:

1、写入过程中,结果实时显示在listview中,窗体不假死。

2、通过设置最大线程数,根据最大线程数并发写入。

3、三个参数通过循环传给参数。

4、写入时,通过顺序写入,目前没按顺序写入。



附上目前的代码,请大佬们指点。

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

namespace ThreadStudy
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < 10000; i++)
{
Person p = new Person(i, "刘备"+i.ToString(), "liubei123456");
ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadFunc),p);
}
}
private delegate void MyInvokeDelegate(object name);
private void Test(object o)
{
Person p = o as Person;
listView1.Items.Add(new ListViewItem(new string[] { p.Id.ToString(), p.Name, Thread.CurrentThread.ManagedThreadId.ToString() }));
}

public void ThreadFunc(object b)
{
MyInvokeDelegate myInvoke = new MyInvokeDelegate(Test);
this.BeginInvoke(myInvoke, b);
}
}
public class Person
{
public Person(int id, string name,string password) { Id = id; Name = name;Password = password; }
public int Id { get; set; }
public string Name { get; set; }
public string Password { get; set; }
}
}
...全文
275 8 打赏 收藏 转发到动态 举报
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
shensi88 2021-02-19
  • 打赏
  • 举报
回复
兔子-顾问 2021-02-19
  • 打赏
  • 举报
回复
private void Test(object o)
{
    Person p = o as Person; 
    listView1.Items.Add(new ListViewItem(new string[] { p.Id.ToString(), p.Name, Thread.CurrentThread.ManagedThreadId.ToString() }));
    listView1.Update();
}
先告诉你为什么,因为你每次更新后,ListView.Item更新了,界面还没有更新,所以要等你所有循环结束,再次进入消息循环才可以刷新显示。那么最直接的做法就是在你的item变化后,调用一次ListView的Update方法。 可是这样做,直接的结果就是闪烁的发生,你根本看不清更新的数据。 进一步的做法就是自己写一个DoubleBufferListView类继承系统的ListView
public class DoubleBufferListView : ListView
{
    public DoubleBufferListView()
    {
        SetStyle(ControlStyles.DoubleBuffer |
            ControlStyles.OptimizedDoubleBuffer |
            ControlStyles.AllPaintingInWmPaint, true);
        UpdateStyles();
    }
}
用这个替换掉你界面的ListView即可,最简单做法直接去修改你的Form1.Designer.cs文件,找到定义,修改为DoubleBufferListView类型。 至此,实现你的效果,并且不闪烁。 另外多给你回答一个,免得你等下还要问 如果你希望新添加的可以优先看到,有滚动的效果,可以增加一行,修改为如下:
private void Test(object o)
{
    Person p = o as Person; 
    listView1.Items.Add(new ListViewItem(new string[] { p.Id.ToString(), p.Name, Thread.CurrentThread.ManagedThreadId.ToString() }));
    listView1.EnsureVisible(listView1.Items.Count - 1);
    listView1.Update();
}
  • 打赏
  • 举报
回复
关于 .net framwork 的问题:.net 4.5 可以运行在xp sp2系统上,已经是很“可以的”了。除非一定要用比 xp 更早的操作系统,还有多少理由非要用6、7年以前的版本呢?
  • 打赏
  • 举报
回复
不论是线程池还是Task方式写代码,道理是一样的,你的 button1_Click 方法本身是一个阻塞的 for 循环,那么循环结束完毕之前主窗体就被卡死了。你需要将这个方法里边的过程也注册给子线程执行,才能让主窗体不卡死。
  • 打赏
  • 举报
回复
写入内容,给你写个例子:
using System;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Test("测试1",10);
            Test("测试2",10);
            Test("测试3",10);
            Test("测试4",10);
            Test("测试5",10);
        }

        Random rnd = new Random();

        async void Test(string title, int numbers)
        {
            for (var i = 1; i <= numbers; i++)
            {
                var n = rnd.Next(1000);
                this.listBox1.Items.Add($"从“{title}”写入第{i}个内容,然后等待{n}毫秒");
                await Task.Delay(n);
            }
        }
    }
}
你可以看到,这里模拟5个“独立并发的”任务,分别循环10此写入内容,它们操纵之间不卡顿,你可以拖动窗体,可以干别的事情。
shensi88 2021-02-19
  • 打赏
  • 举报
回复
引用 3 楼 weixin_49541915 的回复:
[quote=引用 1 楼 兔子党逍遥 的回复:]
private void Test(object o)
{
    Person p = o as Person; 
    listView1.Items.Add(new ListViewItem(new string[] { p.Id.ToString(), p.Name, Thread.CurrentThread.ManagedThreadId.ToString() }));
    listView1.Update();
}
先告诉你为什么,因为你每次更新后,ListView.Item更新了,界面还没有更新,所以要等你所有循环结束,再次进入消息循环才可以刷新显示。那么最直接的做法就是在你的item变化后,调用一次ListView的Update方法。 可是这样做,直接的结果就是闪烁的发生,你根本看不清更新的数据。 进一步的做法就是自己写一个DoubleBufferListView类继承系统的ListView
public class DoubleBufferListView : ListView
{
    public DoubleBufferListView()
    {
        SetStyle(ControlStyles.DoubleBuffer |
            ControlStyles.OptimizedDoubleBuffer |
            ControlStyles.AllPaintingInWmPaint, true);
        UpdateStyles();
    }
}
用这个替换掉你界面的ListView即可,最简单做法直接去修改你的Form1.Designer.cs文件,找到定义,修改为DoubleBufferListView类型。 至此,实现你的效果,并且不闪烁。 另外多给你回答一个,免得你等下还要问 如果你希望新添加的可以优先看到,有滚动的效果,可以增加一行,修改为如下:
private void Test(object o)
{
    Person p = o as Person; 
    listView1.Items.Add(new ListViewItem(new string[] { p.Id.ToString(), p.Name, Thread.CurrentThread.ManagedThreadId.ToString() }));
    listView1.EnsureVisible(listView1.Items.Count - 1);
    listView1.Update();
}
谢谢大佬的用心回复,当前闪烁的问题和更新的问题解决了,但是在写入过程中,主窗口还是会卡死。
引用 1 楼 兔子党逍遥 的回复:
private void Test(object o)
{
    Person p = o as Person; 
    listView1.Items.Add(new ListViewItem(new string[] { p.Id.ToString(), p.Name, Thread.CurrentThread.ManagedThreadId.ToString() }));
    listView1.Update();
}
先告诉你为什么,因为你每次更新后,ListView.Item更新了,界面还没有更新,所以要等你所有循环结束,再次进入消息循环才可以刷新显示。那么最直接的做法就是在你的item变化后,调用一次ListView的Update方法。 可是这样做,直接的结果就是闪烁的发生,你根本看不清更新的数据。 进一步的做法就是自己写一个DoubleBufferListView类继承系统的ListView
public class DoubleBufferListView : ListView
{
    public DoubleBufferListView()
    {
        SetStyle(ControlStyles.DoubleBuffer |
            ControlStyles.OptimizedDoubleBuffer |
            ControlStyles.AllPaintingInWmPaint, true);
        UpdateStyles();
    }
}
用这个替换掉你界面的ListView即可,最简单做法直接去修改你的Form1.Designer.cs文件,找到定义,修改为DoubleBufferListView类型。 至此,实现你的效果,并且不闪烁。 另外多给你回答一个,免得你等下还要问 如果你希望新添加的可以优先看到,有滚动的效果,可以增加一行,修改为如下:
private void Test(object o)
{
    Person p = o as Person; 
    listView1.Items.Add(new ListViewItem(new string[] { p.Id.ToString(), p.Name, Thread.CurrentThread.ManagedThreadId.ToString() }));
    listView1.EnsureVisible(listView1.Items.Count - 1);
    listView1.Update();
}
感谢朋友的解答,窗体闪烁的问题的确可以解决,但是目前的问题,在写入数据过程中,主窗体无响应,无法移动,写入listview的线程号始终是1,也就是说是主线程写入的,并不是子线程更新写入的,如何解决呢。[/quote] 你没下载我发的源码吗,里面有个类是对BackgroundWorker的扩展封装,专门在多线程下更新控件
weixin_49541915 2021-02-19
  • 打赏
  • 举报
回复
引用 2 楼 shensi88 的回复:
源码:https://download.csdn.net/download/shensi88/15364098
感谢朋友实现这个功能,实现的很完美,增加了进度条和线程数,但仍有些问题没解决: 1、更改线程数后,运行时间相同,设置较大的线程数,运行时间较短才对。 2、窗体闪烁,运行过程中无法看清更新的数据。 3、最好在我的基础上改动,我想知道我的问题出在哪里,我用的threadpool,用thread加委托也可以,在.net framework3.0-4.0之间运行,如果用task,需要 在4.5以后的版本。
weixin_49541915 2021-02-19
  • 打赏
  • 举报
回复
引用 1 楼 兔子党逍遥 的回复:
private void Test(object o)
{
    Person p = o as Person; 
    listView1.Items.Add(new ListViewItem(new string[] { p.Id.ToString(), p.Name, Thread.CurrentThread.ManagedThreadId.ToString() }));
    listView1.Update();
}
先告诉你为什么,因为你每次更新后,ListView.Item更新了,界面还没有更新,所以要等你所有循环结束,再次进入消息循环才可以刷新显示。那么最直接的做法就是在你的item变化后,调用一次ListView的Update方法。 可是这样做,直接的结果就是闪烁的发生,你根本看不清更新的数据。 进一步的做法就是自己写一个DoubleBufferListView类继承系统的ListView
public class DoubleBufferListView : ListView
{
    public DoubleBufferListView()
    {
        SetStyle(ControlStyles.DoubleBuffer |
            ControlStyles.OptimizedDoubleBuffer |
            ControlStyles.AllPaintingInWmPaint, true);
        UpdateStyles();
    }
}
用这个替换掉你界面的ListView即可,最简单做法直接去修改你的Form1.Designer.cs文件,找到定义,修改为DoubleBufferListView类型。 至此,实现你的效果,并且不闪烁。 另外多给你回答一个,免得你等下还要问 如果你希望新添加的可以优先看到,有滚动的效果,可以增加一行,修改为如下:
private void Test(object o)
{
    Person p = o as Person; 
    listView1.Items.Add(new ListViewItem(new string[] { p.Id.ToString(), p.Name, Thread.CurrentThread.ManagedThreadId.ToString() }));
    listView1.EnsureVisible(listView1.Items.Count - 1);
    listView1.Update();
}
谢谢大佬的用心回复,当前闪烁的问题和更新的问题解决了,但是在写入过程中,主窗口还是会卡死。
引用 1 楼 兔子党逍遥 的回复:
private void Test(object o)
{
    Person p = o as Person; 
    listView1.Items.Add(new ListViewItem(new string[] { p.Id.ToString(), p.Name, Thread.CurrentThread.ManagedThreadId.ToString() }));
    listView1.Update();
}
先告诉你为什么,因为你每次更新后,ListView.Item更新了,界面还没有更新,所以要等你所有循环结束,再次进入消息循环才可以刷新显示。那么最直接的做法就是在你的item变化后,调用一次ListView的Update方法。 可是这样做,直接的结果就是闪烁的发生,你根本看不清更新的数据。 进一步的做法就是自己写一个DoubleBufferListView类继承系统的ListView
public class DoubleBufferListView : ListView
{
    public DoubleBufferListView()
    {
        SetStyle(ControlStyles.DoubleBuffer |
            ControlStyles.OptimizedDoubleBuffer |
            ControlStyles.AllPaintingInWmPaint, true);
        UpdateStyles();
    }
}
用这个替换掉你界面的ListView即可,最简单做法直接去修改你的Form1.Designer.cs文件,找到定义,修改为DoubleBufferListView类型。 至此,实现你的效果,并且不闪烁。 另外多给你回答一个,免得你等下还要问 如果你希望新添加的可以优先看到,有滚动的效果,可以增加一行,修改为如下:
private void Test(object o)
{
    Person p = o as Person; 
    listView1.Items.Add(new ListViewItem(new string[] { p.Id.ToString(), p.Name, Thread.CurrentThread.ManagedThreadId.ToString() }));
    listView1.EnsureVisible(listView1.Items.Count - 1);
    listView1.Update();
}
感谢朋友的解答,窗体闪烁的问题的确可以解决,但是目前的问题,在写入数据过程中,主窗体无响应,无法移动,写入listview的线程号始终是1,也就是说是主线程写入的,并不是子线程更新写入的,如何解决呢。

110,533

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧