C# 与ManualResetEvent同步时出现问题
我已经编写了一个方法来下载一些文件,现在我正试图让它并行下载多达5个文件,其余的等待前面的文件完成。 我正在为此使用ManualResetEvent,但当我包含同步部分时,它不再下载任何内容(没有它,它就可以工作) 以下是这些方法的代码:C# 与ManualResetEvent同步时出现问题,c#,synchronization,silverlight-4.0,windows-phone-7,C#,Synchronization,Silverlight 4.0,Windows Phone 7,我已经编写了一个方法来下载一些文件,现在我正试图让它并行下载多达5个文件,其余的等待前面的文件完成。 我正在为此使用ManualResetEvent,但当我包含同步部分时,它不再下载任何内容(没有它,它就可以工作) 以下是这些方法的代码: static readonly int maxFiles = 5; static int files = 0; static object filesLocker = new object(); static System.Th
static readonly int maxFiles = 5;
static int files = 0;
static object filesLocker = new object();
static System.Threading.ManualResetEvent sync = new System.Threading.ManualResetEvent(true);
/// <summary>
/// Download a file from wikipedia asynchronously
/// </summary>
/// <param name="filename"></param>
public void DoanloadFileAsync(string filename)
{
...
System.Threading.ThreadPool.QueueUserWorkItem(
(o) =>
{
bool loop = true;
while (loop)
if (sync.WaitOne())
lock (filesLocker)
{
if (files < maxFiles)
{
++files;
if (files == maxFiles)
sync.Reset();
loop = false;
}
}
try
{
WebClient downloadClient = new WebClient();
downloadClient.OpenReadCompleted += new OpenReadCompletedEventHandler(downloadClient_OpenReadCompleted);
downloadClient.OpenReadAsync(new Uri(url, UriKind.Absolute));
//5 of them do get here
}
catch
{
lock (filesLocker)
{
--files;
sync.Set();
}
throw;
}
});
}
void downloadClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
try
{
//but none of the 5 get here
...Download logic... //works without the ManualResetEvent
}
finally
{
lock (filesLocker)
{
--files;
sync.Set();
}
}
}
static readonly int maxFiles=5;
静态int文件=0;
静态对象filesLocker=新对象();
静态System.Threading.ManualResetEvent sync=新的System.Threading.ManualResetEvent(真);
///
///从wikipedia异步下载文件
///
///
public void DoanloadFileAsync(字符串文件名)
{
...
System.Threading.ThreadPool.QueueUserWorkItem(
(o) =>
{
布尔循环=真;
while(循环)
if(sync.WaitOne())
锁(文件锁)
{
if(文件
我做错什么了吗
它是用适用于Windows Phone 7的Silverlight 4编写的。
编辑:Silverlight 4中没有信号灯或信号灯LIM。从外观上看,在创建
网络客户端之前,WaitOne()
被点击。由于调用Set()
的所有代码都在事件处理程序或异常处理程序中,因此它永远不会命中
可能是您错误地将WebClient
代码包含在线程池线程方法中,而线程池线程方法应该在它之外
System.Threading.ThreadPool.QueueUserWorkItem(
(o) =>
{
bool loop = true;
while (loop)
if (sync.WaitOne())
lock (filesLocker)
{
if (files < maxFiles)
{
++files;
if (files == maxFiles)
sync.Reset();
loop = false;
}
}
});
//Have the try catch OUTSIDE the background thread.
try
{
WebClient downloadClient = new WebClient();
downloadClient.OpenReadCompleted += new OpenReadCompletedEventHandler(downloadClient_OpenReadCompleted);
downloadClient.OpenReadAsync(new Uri(url, UriKind.Absolute));
//5 of them do get here
}
catch
{
lock (filesLocker)
{
--files;
sync.Set();
}
throw;
}
System.Threading.ThreadPool.QueueUserWorkItem(
(o) =>
{
布尔循环=真;
while(循环)
if(sync.WaitOne())
锁(文件锁)
{
if(文件
您似乎试图限制可以立即进入关键部分(文件下载)的线程数量。与其尝试手工制作,不如使用系统.Threading.Semaphore
——它就是这么做的 我在评论中的意思是,当您可以使用联锁时,使用慢速锁。而且这样做会更有效
最多5次并行活动下载:
public class Downloader
{
private int fileCount = 0;
private AutoResetEvent sync = new AutoResetEvent(false);
private void StartNewDownload(object o)
{
if (Interlocked.Increment(ref this.fileCount) > 5) this.sync.WaitOne();
WebClient downloadClient = new WebClient();
downloadClient.OpenReadCompleted += downloadClient_OpenReadCompleted;
downloadClient.OpenReadAsync(new Uri(o.ToString(), UriKind.Absolute));
}
private void downloadClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
try
{
// Download logic goes here.
}
catch {}
finally
{
this.sync.Set();
Interlocked.Decrement(ref this.fileCount);
}
}
public void Run()
{
string o = "url1";
System.Threading.ThreadPool.QueueUserWorkItem(this.StartNewDownload, o);
Thread.Sleep(100);
o = "url2";
System.Threading.ThreadPool.QueueUserWorkItem(this.StartNewDownload, o);
o = "url3";
System.Threading.ThreadPool.QueueUserWorkItem(this.StartNewDownload, o);
Thread.Sleep(200);
o = "url4";
System.Threading.ThreadPool.QueueUserWorkItem(this.StartNewDownload, o);
o = "url5";
System.Threading.ThreadPool.QueueUserWorkItem(this.StartNewDownload, o);
o = "url6";
System.Threading.ThreadPool.QueueUserWorkItem(this.StartNewDownload, o);
o = "url7";
System.Threading.ThreadPool.QueueUserWorkItem(this.StartNewDownload, o);
Thread.Sleep(200);
o = "url8";
System.Threading.ThreadPool.QueueUserWorkItem(this.StartNewDownload, o);
Thread.Sleep(400);
}
}
当您可以使用System.Threading.interlocated.Decrement()方法等时,为什么要锁定?因为我也想调用sync.Set(),我想有人从另一个线程冷调用sync.Set(),然后我递减,一些线程递增并调用sync.Reset(),然后我调用sync.Set()我得到了更多的maxFiles线程下载。检查我的答案,这就是你要找的。另外,使用重置事件也可以,我只是不认为需要锁。哦,我使用AutoResteEvent,因为它正是您在这里需要的;对于当前的DownloadAsync调用。这样它一次只下载maxfile,其余的等待前面的一次下载完成。(我在调试时没有收到该部分的NullReferenceException,所以我猜它是正确创建的)。Silverlight for Windows Phone中似乎没有信号灯。还有什么类似于信号灯的东西我可以用吗?啊-是的-对不起-也许没有。我正在查看完整框架的文档。我在这里找到了一些有用的链接,人们可以在Silverlight中讨论替代方案:Rob是正确的;您使用了错误的同步原语。如果您使用的是.NET 4.0,请改用System.Threading.SemaphoreSlim
——它轻量级、快速,并且支持取消令牌,在执行长时间运行的操作时,您可能会发现这一功能很有用