后台工作线程和垃圾回收?

14

我可以在一个方法中定义后台工作吗?

private void DownLoadFile(string fileLocation){
  BackgroundWorker worker = new BackgroundWorker();

  worker.DoWork += new DoWorkEventHandler((obj, args) => { 
      // Will be executed by back ground thread asynchronously.
      args.Result = Download(fileLocation);   
  });

  worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler((obj, args) => { 
      // will be executed in the main thread.
      Result r = args.Result as Result;
      ReportResult(r);
   });

   worker.RunWorkerAsync(fileLocation);
}

问题:如果Download()函数需要长时间来下载文件,GC能否在RunWorkerCompleted()执行之前回收worker对象?


我建议使用委托(Action(Of String) 可以)然后调用 BeginInvoke。我在这段代码中看不到使用 BackgroundWorker 的必要性。你仍然可以为方法完成时附加一个事件。 - JDB
1
更好的方法是使用 QueueUserWorkItem 将其添加到线程池队列中:http://msdn.microsoft.com/zh-cn/library/system.threading.threadpool.queueuserworkitem.aspx - J...
@J... 线程池很好,但它确实使得在正确的同步上下文中处理回调变得更加困难。不过TPL可以很好地处理这个问题。 - Reed Copsey
@ReedCopsey 没错,但TPL仅适用于.NET 4.0+(我想),因此线程池是一种可行的替代方案(OP没有说明框架版本)。 - J...
@J...(或使用Rx框架的3.5版本)- 是的,您需要自己管理SychronizationContext的发布与QUWI。 - Reed Copsey
1个回答

13

鉴于您实际上并没有使用BackgroundWorker的大部分功能,我建议改用TPL来完成:

private void DownLoadFile(string fileLocation)
{
    Task.Factory.StartNew( () => Download(fileLocation))
        .ContinueWith(t => ReportResult(t.Result), TaskScheduler.FromCurrentSynchronizationContext());
}

话虽如此,worker 对象在运行时不会被垃圾回收,因为线程池线程本身将保持 worker 作为“已使用的对象”。垃圾收集器直到完成事件处理程序执行后才能进行回收,在此时段内没有用户代码可以访问 BackgroundWorker 实例。

另外,由于闭包使用的实例方法(ReportResults)使得 "this" 实例可访问且不符合 GC 条件,因此很可能会保留该类的实例,而不被垃圾回收。


这并不是对楼主问题的真正回答,但仍然是很好的建议。 - JDB
1
@Cyborgx37 给原帖的问题添加了直接回答。 - Reed Copsey
然而,让这样的变量超出范围真的是个好主意吗?接替他的程序员会期望看到一个在范围内的变量被引用,而不是一些在某个地方的僵尸变量。 - Robert Harvey
@RobertHarvey 不行 - 这就是为什么我建议在这里切换模型。我绝不会以这种方式使用BW。它能工作,但像那样使用它的代码确实很奇怪。(话虽如此,它与直接使用ThreadPool.QUWI并没有太大的区别...) - Reed Copsey
@ReedCopsey - 谢谢,你不仅证实了我的闭包理论,还提出了更好的方法 - 非常感谢。+1 - karephul

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接