AutoResetEvent使用问题

时间:2013-07-12 20:02:45

标签: c# .net multithreading autoresetevent

我正在尝试使用AutoResetEvent对象来阻止线程,直到异步。下载了一个WebClient。

我的问题是,一旦我调用WaitOne(),线程就会锁定并且VS永远不会到达DownloadComplete事件处理程序方法中的断点。

这是我的代码

//Class used to pass arguments to WebClient's events...

public class RunArgs
{
    public JobInfo jobInfo;
    public int jobTotal;
    public int jobIndex;
    public AutoResetEvent AutoResetEventObject;
}

List<JobInfo> jl = ConfigSectionWrapper.GetAllJobs();

int jobAmount = jl.Count;
int jobIndex = 0;

RunArgs args = new RunArgs();
args.jobTotal = jl.Count;


foreach (JobInfo ji in jl)
{


    if (ji.enabled == "0")
    {
        args.jobIndex++;
        continue;
    }

    try
    {
        args.jobIndex++;
        args.jobInfo = ji;

        appLog.Source = ji.eventSource;
        appLog.WriteEntry(string.Format("Started job {0}...", ji.jobName),         EventLogEntryType.Information);
        ji.fullFileName = string.Format(ji.reportFileName, string.Format("{0}-{1}-{2}", DateTime.Now.Year.ToString(), DateTime.Now.Month.ToString().PadLeft(2, '0'), DateTime.Now.Day.ToString().PadLeft(2, '0')));
        ji.fullFileName = string.Format("{0}{1}", ji.downloadDirectory, ji.fullFileName);

        using (WebClient wc = new WebClient())
        {
            AutoResetEvent notifier = new AutoResetEvent(false);
            args.AutoResetEventObject = notifier;
            wc.Credentials = CredentialCache.DefaultNetworkCredentials;
            wc.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadCompleted);
            wc.DownloadFileAsync(new Uri(args.jobInfo.reportURL), args.jobInfo.fullFileName, args); //Pass the args params to event handler...
            notifier.WaitOne();
         }
    }
    catch (Exception ex)
    {
        appLog.WriteEntry(string.Format("Error starting report execution: {0}", ex.Message), EventLogEntryType.Error);
        DeleteFile(ji.fullFileName);
    }

}

private void DownloadCompleted(object sender, AsyncCompletedEventArgs e)
{

    RunArgs args = (RunArgs)e.UserState;

    //Do things....

    args.AutoResetEventObject.Set();  
 }

因此我在构造函数中使用false实例化 通知程序 ,因为我不希望其状态已经发出信号。除非我读错了MSDN?

显然有什么不对吗?

3 个答案:

答案 0 :(得分:2)

WebClient使用AsyncOperationManager来管理异步操作。因此,请确保您了解在不同情况下如何调用这些异步操作。

在WinForms下

WinForm申请AsyncOperationManager下使用WindowsFormsSynchronizationContext。正如@Michael所说,WaitOne()调用阻止主表单线程触发DownloadCompleted事件。在WinForms中,DownloadCompleted在主WinForm主题上执行。

所以,删除notifier.WaitOne()它应该有效。 DownloadCompleted需要由主窗口线程调用(可能是您使用WaitOne()阻止的那个)。

在控制台应用程序

Console类型的应用程序下,AsyncOperationManager使用System.Threading.SynchronizationContext,而DownloadCompleted由线程池中的线程异步执行。

notifier.WaitOne()应用下调用Console没有问题;并且上面的代码按预期工作。

答案 1 :(得分:1)

我还没有找到任何文档来支持这一点,但是看一下Reflector中的WebClient代码,似乎事件是在主线程而不是后台线程上引发的。由于在调用notifier.WaitOne()时主线程处于阻塞状态,因此永远不会调用事件处理程序。

如果您提供的示例准确代表您的代码,则绝对不需要使用wc.DownloadFileAsync()代替wc.DownloadFile()notifier.WaitOne()调用最终使其成为同步操作。如果您正在寻找真正的异步操作,则必须采用不同的方式。

答案 2 :(得分:0)

这是我最终做的事情:

private AutoResetEvent notifier = new AutoResetEvent(false);

现在主循环看起来像:

        foreach (JobInfo ji in jl)
        {
            if (ji.enabled == "0")
            {
                args.jobIndex++;
                continue;
            }

            args.jobInfo = ji;
            Thread t = new Thread(new ParameterizedThreadStart(startDownload));
            t.Start(args);
            notifier.WaitOne();
        }

private void startDownload(object startArgs)
    {
        RunArgs args = (RunArgs)startArgs;

        try
        {
            args.jobIndex++;
            appLog.Source = args.jobInfo.eventSource;
            appLog.WriteEntry(string.Format("Started job {0}...", args.jobInfo.jobName), EventLogEntryType.Information);
            args.jobInfo.fullFileName = string.Format(args.jobInfo.reportFileName, string.Format("{0}-{1}-{2}", DateTime.Now.Year.ToString(), DateTime.Now.Month.ToString().PadLeft(2, '0'), DateTime.Now.Day.ToString().PadLeft(2, '0')));
            args.jobInfo.fullFileName = string.Format("{0}{1}", args.jobInfo.downloadDirectory, args.jobInfo.fullFileName);

            WebClient wc = new WebClient();

            wc.Credentials = CredentialCache.DefaultNetworkCredentials;
            wc.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadCompleted);
            wc.DownloadFileAsync(new Uri(args.jobInfo.reportURL), args.jobInfo.fullFileName, args); //Pass the args params to event handler...

        }
        catch (Exception ex)
        {
            appLog.WriteEntry(string.Format("Error starting report execution: {0}", ex.Message), EventLogEntryType.Error);
            DeleteFile(args.jobInfo.fullFileName);
            notifier.Set();
        }

    }

所以现在AutoResetEvent阻止了主线程,但 t 可以成功触发 DownloadFileCompleteEvent 。在 DownloadFileCompleted 事件中,我显然也在做 notifier.Set()

全部谢谢!