8

私は多くのイベントとスレッドの議論を読んでいますが、イベントの購読を解除して後でそれを呼び出そうとした場合に「何が起こるか」に焦点を当てています。私の質問は異なります...ミリ秒1でイベント「終了」を発生させるプロセスがスレッドAにあり、ミリ秒2でイベント「終了」を発生させるプロセスがスレッドBにあるとどうなりますか.

どちらのプロセスも、イベントをリッスンして処理する同じメソッドにサブスクライブされています。そのため、C# はイベントを処理するメソッドを 2 回実行する必要があります。1 回はスレッド A で発生したイベントで、1 回はスレッド B で発生したイベントです。

何が起こるか??「スレッドAからの最初のイベント」がイベントを処理するメソッドの実行を開始すると、C#はメソッドをロックし、実行が終了するとメソッドをロック解除して、他の待機中の「イベント」がメソッドコンテンツを実行できるようにしますか??

または、スレッド A から起動されたイベントが、そのイベントを処理するメソッドの実行を開始し、1 ミリ秒後に、スレッド B から起動されたイベントも同じメソッドで実行を開始します。処理する"???

イベントをキャッチするメソッドでファイル書き込みを行いたいので、これを求めていますが、メソッドが同時に実行できる場合(イベントが発生するタイミングによって異なります)、ここでは実行できないと思います。ファイルは、同時に同じファイルに書き込む 2 つのプロセス間の混合になります (ファイルに関する有効な情報ではありません)。

私のコードは次のようになります (少し長くなってすみません)。これはコンパイルされないことに注意してください。これは、私が何をしているかを示す単なるサンプルです。

 public partial class MainForm : Form
{
    FTPClientManager client = null;

    public MainForm()
    {
        InitializeComponent();
    }

    private void btnConnect_Click(object sender, EventArgs e)
    {
        Connect(this.tbFTPServer.Text.Trim());
        this.lstLog.Items.Add("Connected"); //This lstLog is a list box that will have a list of downloaded files from all threads.
    }

    void Connect(string urlStr)
    {
        try {
            client = new FTPClientManager();
            //subscribe to the event
            client.FileDownloadCompleted += new EventHandler<FileDownloadCompletedEventArgs>(client_FileDownloadCompleted);
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

    void client_FileDownloadCompleted(object sender, FileDownloadCompletedEventArgs e)
    {
        this.Invoke(new EventHandler<FileDownloadCompletedEventArgs>(
             client_FileDownloadCompletedHandler), sender, e);
    }

    void client_FileDownloadCompletedHandler(object sender, FileDownloadCompletedEventArgs e)
    {
        string log = string.Format("{0} Instance {5} Download from {1} to {2} is completed. Length: {3} Time: {4}. ",
            DateTime.Now, e.ServerPath, e.LocalFile.FullName, e.LocalFile.Length, e.DownloadTime, e.ftpInstance);

        this.lstLog.Items.Add(log);
    }

    private void btnDownload_Click(object sender, EventArgs e)
    {
        client.DownloadFiles();
    }
}   

public class FTPClientManager {
    FTPDownloadClient[] arrayDownloadClient = new FTPDownloadClient[2];        
    public event EventHandler<FileDownloadCompletedEventArgs> FileDownloadCompleted;

    public void DownloadFiles()
    {
        for (int i = 0; i < 2; i++)
        {
            arrayDownloadClient[i] = new FTPDownloadClient();
            //subscribe to the event. each instance of FTPDownloadClient will suscribe to the same event.
            arrayDownloadClient[i].FileDownloadCompleted += new EventHandler<FileDownloadCompletedEventArgs>(downloadClient_FileDownloadCompleted);
        }

        //download one set of files in thread A
        arrayDownloadClient[0].DownloadFiles(list_of_files_to_download);

        //download another set of files in thread B
        arrayDownloadClient[1].DownloadFiles(another_list_of_files_to_download);
    }

    //In theory, the method downloadClient_FileDownloadCompleted will be executed by any instance of FTPDownloadClient
    //running in either thread A or thread B, whichever finish first downloading a file.
    //My question comes in the execution of this method.
    //Lets say the process in thread A finish downloading and fires the event.
    //Lets say the process in thread B finish downloading 1 millisecond after thread A finish, so it also fires the event.
    //how C# manage the execution of the downloadClient_FileDownloadCompleted??
    //does the event coming from thread A will lock the method downloadClient_FileDownloadCompleted, execute it, and when finish execution unlock the method 
    //and allows the event coming from thread B start locking, processing, unlock ??
    //Or the method will be executed "at the same time" (1 millisecond difference) by each event fired from thread A and thread B??
    void downloadClient_FileDownloadCompleted(object sender, FileDownloadCompletedEventArgs e)
    {
        this.OnFileDownloadCompleted(e);
    }

    protected virtual void OnFileDownloadCompleted(FileDownloadCompletedEventArgs e)
    {
        if (FileDownloadCompleted != null)
        {
            //this will fire the event, so the main form will catch it
            //again, this fire can be triggered from process in thread A or from process in thread B
            FileDownloadCompleted(this, e); 
        }
    }
}

public class FTPDownloadClient {
    public event EventHandler<FileDownloadCompletedEventArgs> 
            FileDownloadCompleted;

    public void DownloadFiles(string [] files_to_download)
    {
        ParameterizedThreadStart threadStart =
                new ParameterizedThreadStart(StartDownloadFiles);
            Thread downloadThread = new Thread(threadStart);
            downloadThread.IsBackground = true;
            downloadThread.Start(new object[] { files_to_donwload });
    }

    //This metod will download all the files in the list passed as parameter.
    //Every file downloaded will raise the event FileDownloadComplete, so a message can be added to the lstlog on the main form
    void StartDownloadFiles(object state)
        {
            var paras = state as object[];

            string [] files = paras[0] as string [];

            foreach (var file in files)
            {
                DownloadOneFile(file);
            }
        }

     void DownloadFile(string onefile)
     {
            //Donwload file done here
            var fileDownloadCompletedEventArgs = new FileDownloadCompletedEventArgs
            {
               LocalFile = new FileInfo(destPath),
               ServerPath = onefile,
               DownloadTime = fileDownloadTime.ElapsedMilliseconds.ToString()
            };

            this.OnFileDownloadCompleted(fileDownloadCompletedEventArgs);
     }

     protected virtual void OnFileDownloadCompleted(FileDownloadCompletedEventArgs e)
        {
            if (FileDownloadCompleted != null)
            {
                //the event is fired when the file being downloaded by this thread is finish.
                //so, thread A will fire this event from its current thread
                //and also thread B will fire the same event from its own thread.
                FileDownloadCompleted(this, e); 
            }
        }
}
4

3 に答える 3

0

Form.Invokeメソッドでを使用しているため、コードは既にスレッドセーフになっているようですclient_FileDownloadCompleted

FTPClientManager.FileDownloadCompletedイベントが異なるスレッドで同時に発生する可能性があることは事実ですがForm.Invoke、メインの UI スレッドへのすべての呼び出しがシリアル化されます。したがって、コードではロックを必要とせず、Form.Invoke常にclient_FileDownloadCompletedHandlerUI スレッドで呼び出されます。

于 2013-04-25T14:02:33.757 に答える