65.9K
CodeProject 正在变化。 阅读更多。
Home

创建同步对象

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.75/5 (4投票s)

2009年8月25日

CPOL

1分钟阅读

viewsIcon

43660

downloadIcon

702

如何使用 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 日:文章发布。
© . All rights reserved.