上一篇已经实现了异步功能,不过数据是通过控制台输出。如果需要在控件中显示的话,那么就涉及到跨线程调用控件的问题了
winform规定 不能跨线程访问其他线程创建的对象
以下是我修改后的代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ZQ学习demo
{
public partial class 异步编程 : Form
{
public delegate string delegateObj(string paht);
public 异步编程()
{
InitializeComponent();
delobj = new delegateObj(copyFile);
}
delegateObj delobj=null;
/// <summary>
/// 同步拷贝文件,并输出文件名称
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{
String[] paths = Directory.GetFiles(@"F:\迅雷下载");
for (int i = 0; i < paths.Length; i++)
{
this.listBox1.Items.Add(copyFile(paths[i]));
}
}
/// <summary>异步拷贝文件,并输出文件名
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button2_Click(object sender, EventArgs e)
{
String[] paths = Directory.GetFiles(@"F:\迅雷下载");
for (int i = 0; i < paths.Length; i++)
{
delobj.BeginInvoke(paths[i], null, null);//最后一个参数是回调状态
}
}
public void callback(IAsyncResult result)
{
//if (this.listBox1.InvokeRequired)
//{
// this.listBox2.Invoke(delobj);
// this.listBox2.Items.Add(result.AsyncState.ToString() + "复制成功");
//}
//delobj.EndInvoke(result);
//this.listBox2.Items.Add(result.AsyncState.ToString() + "复制成功");
}
/// <summary>
/// 拷贝文件用
/// </summary>
/// <param name="filename"></param>
public string copyFile(string filename)
{
if (filename.EndsWith("desktop.ini"))
return "desktop.ini";
if (File.Exists(@"F:\迅雷下载2\" + Path.GetFileName(filename)))
{
File.Delete(@"F:\迅雷下载2\" + Path.GetFileName(filename));
}
File.Copy(filename, @"F:\迅雷下载2\" + Path.GetFileName(filename));
if(this.listBox2.InvokeRequired)
{
Action<string> de=(x)=>{this.listBox2.Items.Add(Path.GetFileName(x));};
this.listBox2.Invoke(de, new string[] { filename });
}else
{
this.listBox2.Items.Add(Path.GetFileName(filename));
}
return Path.GetFileName(filename);
}
}
}
跨线程调用控件的其他解决办法(转 https://www.cnblogs.com/TankXiao/p/3348292.html)
第一种办法:禁止编译器对跨线程访问做检查
这是最简单的办法, 相当于不检查线程之间的冲突,允许各个线程随便乱搞,最后Lable1控件的值是什么就难以预料了 (不推荐使用这种方法)
public Form1()
{
InitializeComponent();
// 加入这行
Control.CheckForIllegalCrossThreadCalls = false;
}
第二种办法: 使用delegate和invoke来从其他线程中调用控件
调用控件的invoke方法,就可以控制控件了,例如
private void button2_Click(object sender, EventArgs e)
{
Thread thread1 = new Thread(new ParameterizedThreadStart(UpdateLabel2));
thread1.Start("更新Label");
}
private void UpdateLabel2(object str)
{
if (label2.InvokeRequired)
{
// 当一个控件的InvokeRequired属性值为真时,说明有一个创建它以外的线程想访问它
Action<string> actionDelegate = (x) => { this.label2.Text = x.ToString(); };
// 或者
// Action<string> actionDelegate = delegate(string txt) { this.label2.Text = txt; };
this.label2.Invoke(actionDelegate, str);
}
else
{
this.label2.Text = str.ToString();
}
}