.NET Web Service和BackgroundWorker线程

14
我正在尝试在Web服务方法中执行一些异步操作。假设我有以下API调用:http://www.example.com/api.asmx,并且该方法名为GetProducts()
在这个GetProducts()方法中,我会做一些事情(例如从数据库获取数据),然后在返回结果之前,我想要执行一些异步操作(例如发送电子邮件)。
因此,这是我所做的。
[WebMethod(Description = "Bal blah blah.")]
public IList<Product> GetProducts()
{
    // Blah blah blah ..
    // Get data from DB .. hi DB!
    // var myData = .......
    // Moar clbuttic blahs :)  (yes, google for clbuttic if you don't know what that is)

    // Ok .. now send me an email for no particular reason, but to prove that async stuff works.
    var myObject = new MyObject();
    myObject.SendDataAsync();

    // Ok, now return the result.
    return myData;
    }
}

public class TrackingCode
{
    public void SendDataAsync()
    {
        var backgroundWorker = new BackgroundWorker();
        backgroundWorker.DoWork += BackgroundWorker_DoWork;
        backgroundWorker.RunWorkerAsync();
        //System.Threading.Thread.Sleep(1000 * 20);
    }

    private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        SendEmail();
    }
}

现在,当我运行这段代码时,电子邮件从未发送。如果我取消注释Thread.Sleep ..然后电子邮件就会发送。

那么...为什么后台工作线程被关闭?它是否依赖于父线程?在asp.net web应用程序中,这是我应该使用的错误方式吗?

3个回答

18

BackgroundWorker 用于需要将操作同步到(例如)UI线程的情况,例如出于亲和性原因。在这种情况下,似乎仅使用 ThreadPool 就已经足够了(而且更加简单)。如果您有大量工作,则生产者/消费者队列可能会允许更好的节流(以便您不会淹没在线程中)-但我认为在这里ThreadPool 将是可以胜任的...

public void SendDataAsync()
{
    ThreadPool.QueueUserWorkItem(delegate
    {
        SendEmail();
    });
}

此外,我不太确定你想通过睡眠实现什么?这只会绑定一个线程(不使用CPU,但也无益处)。能详细说明一下吗?看起来你是在暂停你的实际网页(即Sleep发生在网页线程而非电子邮件线程上)。你在这里尝试做什么?

*=实际上,它将使用任何已经存在的同步上下文


睡眠已被注释掉。我将它放在那里,以查看是否可以发送电子邮件。当取消注释的时候,后台工作线程代码将被执行。当代码被注释掉时,后台工作线程代码将永远不会被执行。我将尝试使用线程池而不是这种方法。 - Pure.Krome
如果您有高并发量,则生产者/消费者队列可能会允许更好的节流控制(以便您不会被线程淹没)。哦哦哦哦哦!我很高兴您实际上提出了这个问题!Web服务方法实际上是高容量的!而且,为了使事情更加有趣,它实际上并不是发送电子邮件,而是访问另一个外部网站(呃,请不要问)......所以您建议我做这个生产者/消费者队列吗?如果是这样......这是一个好的参考页面吗?http://www.albahari.com/threading/part4.aspx(请向下滚动一段时间至代码+示例)。您有什么想法? - Pure.Krome
@Marc Gravell,我已经尝试使用您的解决方案,但我想知道为什么完成任务需要太长时间。 - AnandMohanAwasthi
@AnandMohanAwasthi 嗯...你的具体任务是什么?异步运行并不会使它更快... - Marc Gravell
例如,假设我需要向2000个ID发送相同的邮件。如果我使用经典的方式on_click事件来执行此操作,则此活动大约需要5-6分钟时间,但是如果我使用ThreadPool QueueUserWorkItem委托,则实际上会花费更长的时间...(新处理线程)..请帮忙。 - AnandMohanAwasthi
@AnandMohanAwasthi,你没有说明你是如何发送邮件的,那么......?大多数情况下,UI线程和工作线程之间没有区别。如果代码需要同步上下文或与COM STA通信,则需要更多步骤。但是你没有给出任何提示,所以我甚至无法猜测。 - Marc Gravell

1

生产者/消费者;基本上,只值得保留某种形式的节流。在最简单的情况下,可以使用Semaphore(以及常规的ThreadPool)来限制已知数量的工作(以避免饱和线程池);但是,生产者/消费者队列可能会更高效和受控。

Jon Skeet拥有这样一个队列hereCustomThreadPool)。如果您想要,我可以写一些关于它的注释。

话虽如此:如果您正在调用外部网站,则很可能会等待大量网络IO /完成端口;因此,您可以拥有稍高数量的线程...显然(相比之下),如果工作是CPU限制的,则没有必要超过您拥有的CPU核心数量的线程。


0

它可能会被拆除,因为在20秒后,该BackgroundWorker实例可能会被垃圾回收,因为它没有引用(超出范围)。


请注意,Sleep() 函数在网页线程上执行;很可能是 BackgroundWorker 尝试使用被 Sleep() 函数本身阻塞的同步上下文。 - Marc Gravell

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