我试图使用一个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();
}因此,我在构造函数中实例化了通知程序,因为我不希望它的状态已经发出信号。除非我看错了MSDN?
有什么明显的问题吗?
发布于 2013-07-15 14:33:29
以下是我最后所做的:
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();
}
}因此,现在DownloadFileCompleteEvent.正在阻塞主线程,但是t可以成功地触发该在DownloadFileCompleted事件中,我显然也在做一个notifier.Set()。
谢谢大家!
发布于 2013-07-12 20:54:50
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没有问题;上面的代码就像预期的那样工作。
发布于 2013-07-12 20:14:06
我还没有找到任何文档来支持这一点,但是查看Reflector中的WebClient代码,事件似乎是在主线程上引发的,而不是在后台线程中引发的。由于主线程在调用notifier.WaitOne()时是阻塞的,所以事件处理程序永远不会被调用。
如果您提供的示例准确地表示了您的代码,那么绝对没有必要使用wc.DownloadFileAsync()而不是wc.DownloadFile()。notifier.WaitOne()调用最终使其成为同步操作。如果您正在寻找真正的异步操作,则必须以不同的方式执行此操作。
https://stackoverflow.com/questions/17623113
复制相似问题