创建同步对象






4.75/5 (4投票s)
如何使用 ISynchronizeInvoke 接口创建同步对象,就像 FileSystemWatcher 中那样。

引言
你是否曾经需要一个 SynchronizingObject 用于多线程应用程序?
假设你想像这个演示程序那样列出文件,并且从一个与 UI 运行线程不同的工作线程获取文件,那么你将会遇到跨线程异常,这会导致你编写额外的代码,仅仅是为了解决它,对吧?
如果你熟悉 FileSystemWatcher 类,你可能已经注意到 SynchronizingObject,你可以将你的 UI 表单分配给它,从而避免跨线程异常。
很好,SynchronizingObject 的类型是 ISynchronizeInvoke,所有 System.Windows.Forms.Control 都从它派生。这意味着所有控件都可以添加到此属性中。不错吧?
背景
一位朋友问我关于跨线程异常的问题。
使用代码
这个演示程序没有实现 ISynchronizeInvoke 接口,它会将它用作自定义类上的一个属性。
如果你想了解更多关于实现 ISynchronizeInvoke 接口的信息,我推荐这篇不错的 CP 文章:https://codeproject.org.cn/KB/cs/delegatequeue.aspx。
好的,回到我的文章,希望如此;o)
FileEventArgs 类
它非常简单。它包含有关我的自定义列表类将返回的文件总数的信息,文件在列表中的当前位置,以及当前文件的文件信息。
public class FileEventArgs : EventArgs
{
    int _current;
    int _total;
    FileSystemInfo _file;
    public int Current
    {
        get { return _current; }
    }
    public int Total
    {
        get { return _total; }
    }
    public FileSystemInfo File
    {
        get { return _file; }
    }
    internal FileEventArgs(int current, int total, FileSystemInfo file)
    {
        _current = current;
        _total = total;
        _file = file;
    }
}
FileEventHandler 委托
它将用于我的自定义列表类上的事件。
public delegate void FileEventHandler(object sender, FileEventArgs e); 
FileLister 类
我在代码中添加了注释...
public class FileLister
{
    public event FileEventHandler IncommingFile;
    //Event that handles incomming files...
    public event EventHandler FileListDone;
    //Event that handles when list is done... 
    private Thread BackGroundThread; //local background worker.
    private ISynchronizeInvoke synchronizingObject;
    //Contains the object to Sync up against.
    [Browsable(false), DefaultValue((string)null)]
    public ISynchronizeInvoke SynchronizingObject
    //Property to get or set the object to Sync up against.
    {
        get { return this.synchronizingObject; }
        set { this.synchronizingObject = value; }
    }
    protected void OnIncommingFile(FileEventArgs e)
    {
        if (this.IncommingFile != null)
        //if the IncommingFile event is not null. 
        //(meaning that an subscriber to the event is present...)
        {
            //SynchronizingObject is not null and the Synchronizing
            //Object Requires and invokation...
            if ((this.SynchronizingObject != null) && 
                          this.SynchronizingObject.InvokeRequired) 
                this.SynchronizingObject.BeginInvoke(IncommingFile, 
                                         new object[] { this, e });
                //Get the Synchronizing Object to invoke the event...
            else
                IncommingFile(this, e); //Fire the event
        }
    }
    protected void OnFileListDone()
    {
        if (this.FileListDone != null)
        //if the FileListDone event is not null.
        //(meaning that an subscriber to the event is present...)
        {
            //SynchronizingObject is not null and 
            //the Synchronizing Object Requires and invokation...
            if ((this.SynchronizingObject != null) && 
                      this.SynchronizingObject.InvokeRequired)
                this.SynchronizingObject.BeginInvoke(FileListDone, 
                   new object[] { this, null });
                   //Get the Synchronizing Object to invoke the event...
            else
                FileListDone(this, null); //Fire the event
        }
    }
    public void ListFilesFrom(string path)
    {
        //create and BackgroundWorker thread, with parameters...
        BackGroundThread = 
           new Thread(new ParameterizedThreadStart(getFilesInNewThread));
        BackGroundThread.Start(path); //Start the Thead with the parameter...
    }
    private void getFilesInNewThread(object path)
    {
        //get the parameter which is the path 
        //to where the list should list files from.
        string folderpath = path.ToString();
        if (Directory.Exists(folderpath))
        //if the folder exists...
        {
            //Create an new instance of the DirectoryInfo class, 
            //which can get an array if FileSystemInfo objects.
            DirectoryInfo di = new DirectoryInfo(folderpath);
            //Get the FileSystemInfo objects...
            FileSystemInfo[] files = di.GetFileSystemInfos();
            int total = files.Length; //Get total cound of files to send to GUI.
            for (int i = 0; i < total; i++) // loop through the array..
                OnIncommingFile(new FileEventArgs(i, total, files[i]));
                //Send one file at the time through the event.
             //(NOTE: normaly this would not be the case, as one would send
             //       the hole array in one piece. but for demo reason this is so...)
        }
        OnFileListDone(); //Fire the Event saying the loop is done...
    }
    public void StopFilelistEnumeration()
    {
        if (BackGroundThread != null && BackGroundThread.IsAlive)
        //if the backgroundworker thread is not null and is running then...
            BackGroundThread.Abort(); //Abort/kill the thread.
    }
}
请查看演示程序!!!
有关如何使用该类及其 SynchronizingObject 的更多信息,请参阅演示程序。
关注点
通过这个小示例,我的朋友能够创建更线程安全的类。
希望你能够使用它...
历史
- 2009 年 8 月 25 日:文章发布。


