通知事件订阅者自己的/创建线程

本文关键字:创建 线程 自己的 事件 通知 | 更新日期: 2023-09-27 18:18:32

我有一个名为DirectoryCopier的类,它暴露了进度事件,其中我的windows窗体对象将订阅此事件,然后调用CopyFolderAsync开始复制&通知不同线程的事件处理程序。

class DirectoryCopier
{
    public event EventHandler<CopyProgressChangedEventArgs> CopyProgressChanged;
    public void CopyFolderAsync()
    {
        Task.Run(() => CopyFolder(Source, Destination));
    }
    void CopyFolder(string src, string des)
    {
        string srcName = Path.GetFileName(src);
        string desDir = Path.Combine(des, srcName);
        Directory.CreateDirectory(desDir);
        var files = Directory.EnumerateFiles(src);
        foreach (string file in files)
        {
            string fileName = Path.GetFileName(file);
            string desFile = Path.Combine(desDir, fileName);
            CopyFile(file, desFile);
        }
        var dirs = Directory.EnumerateDirectories(src);
        foreach (string dir in dirs)
            CopyFolder(dir, desDir);
    }
    void CopyFile(string src, string dest)
    {
        using (FileStream input = new FileStream(src, FileMode.Open, FileAccess.Read, FileShare.None, buffer.Length))
        using (FileStream output = new FileStream(dest, FileMode.Create, FileAccess.Write, FileShare.None, buffer.Length))
        {
            byte[] buffer = new byte[1024 * 1024];
            int bytesRead;
            do
            {
                bytesRead = input.Read(buffer, 0, buffer.Length);
                output.Write(buffer, 0, bytesRead);
                if (CopyProgressChanged != null)
                    CopyProgressChanged.BeginInvoke(this, new CopyProgressChangedEventArgs(,,,),null,null);
            }
            while (bytesRead > 0);
        }
    }
}

在我的表单的事件处理程序中,我用收到的事件参数更新进度条,当我运行时,这工作得很好。

        progressBar1.Value = e.ProgressPercentage;

当我调试我的应用程序时,进度条值正常更新,但在我的表单中不先进,并且在IntelliTrace窗格中捕获了许多跨线程异常。我可以使用Form.Invoke()来更新progressBar,但这是事件处理程序的责任吗?是否有办法在每个订阅者自己的/创建线程上调用每个订阅者?

通知事件订阅者自己的/创建线程

问题不在于调用创建线程上的每个订阅者,而在于绑定到UI线程的UI控件。

我在这里找到了一个很好的扩展方法,它试图为每个委托提取SynchronizationContext。如果存在,它将使用BeginInvoke调用委托:

public static object Raise(this MulticastDelegate 

multicastDelegate,对象发送者,EventArgs{

    MulticastDelegate threadSafeMulticastDelegate = multicastDelegate;
    if (threadSafeMulticastDelegate != null)
    {
        foreach (Delegate d in threadSafeMulticastDelegate.GetInvocationList())
        {
            var synchronizeInvoke = d.Target as ISynchronizeInvoke;
            if ((synchronizeInvoke != null) && synchronizeInvoke.InvokeRequired)
            {
                retVal = synchronizeInvoke.EndInvoke(synchronizeInvoke.BeginInvoke(d, new[] { sender, e }));
            }
            else
            {
                retVal = d.DynamicInvoke(new[] { sender, e });
            }
        }
    }
    return retVal;
}

}

你可以像这样在你的事件中使用它:

MyEvent.Raise(sender, eventArgs);