首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >与ManualResetEvent同步问题

与ManualResetEvent同步问题
EN

Stack Overflow用户
提问于 2010-07-20 08:45:11
回答 3查看 3.7K关注 0票数 3

我已经编写了一个下载一些文件的方法,现在我试图让它并行下载多达5个文件,其余的则等待前面的文件完成。我为此使用了一个ManualResetEvent,但是当我包含同步部分时,它就不会再下载任何东西了(如果没有它,它就能工作)。

以下是这些方法的代码:

代码语言:javascript
复制
    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();
            }
        }
    }

我做错了什么吗?

它是用Silverlight 4为Windows 7.编写的

编辑:在Silverlight 4.中没有信号量或SemaphoreSlim

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2010-07-20 10:20:00

我在评论中的意思是,当您可以使用lock时,可以使用一个缓慢的Interlocked。而且这样会更有表现力。

最多5次并行活动下载:

代码语言:javascript
复制
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);
 }
}
票数 4
EN

Stack Overflow用户

发布于 2010-07-20 09:04:40

似乎您正在尝试限制可以同时进入关键部分(文件下载)的线程数量。而不是试图手工制作这个,使用一个System.Threading.Semaphore -这就是它的作用!

票数 1
EN

Stack Overflow用户

发布于 2010-07-20 09:03:55

从外观上看,WaitOne()在创建WebClient之前就被击中了。由于调用Set()的所有代码都在事件处理程序或异常处理程序中,所以它永远不会命中。

可能您错误地将WebClient代码包含在线程池线程方法中,而该方法应该在线程池线程方法之外。

代码语言:javascript
复制
    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;
            }
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3288307

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档