我正在尝试使用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?
显然有什么不对吗?
答案 0 :(得分:2)
WebClient
使用AsyncOperationManager
来管理异步操作。因此,请确保您了解在不同情况下如何调用这些异步操作。
在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()。
全部谢谢!