TPL使用单独的类进行消防和忘记

时间:2012-09-02 18:46:30

标签: c# .net task-parallel-library

我正在尝试使用任务并行库实现激活并忘记功能。通过对Task.Factory.StartNew的内联调用,一切都按预期工作。但是,我想将Task.Factory.StartNew调用移动到一个单独的类中,以便我可以添加日志记录,错误处理等,并可能在将来升级代码,因为更好的线程类等添加到.NET Framework中,没有重复的代码。

以下是我希望通过的单元测试,但事实并非如此。我很感激帮助试图弄清楚如何使这项工作。

[TestFixture]
public class ThreadingServiceFixture
{
    public static bool methodFired = false;

    [Test]
    public void CanFireAndForgetWithThreadingService()
    {
        try
        {
            var service = new ThreadingService();

            service.FireAndForget(() => methodFired = true);

            var endTime = DateTime.Now.AddSeconds(1);

            while(DateTime.Now < endTime)
            {
                //wait
            }

            Assert.IsTrue(methodFired == true);
        }
        finally
        {
            methodFired = false;
        }       
    }

}

public class ThreadingService
{
    public Task FireAndForget(Action action)
    {
        return Task.Factory.StartNew(() => action);
    }
}

4 个答案:

答案 0 :(得分:3)

你没有执行动作,你只是回来了。

尝试:

return Task.Factory.StartNew(() => action());

答案 1 :(得分:2)

您未在ThreadingService

中调用该操作

代码应该是类似

的内容
public class ThreadingService
{
    public Task FireAndForget(Action action)
    {
        return Task.Factory.StartNew(() => action.Invoke());
    }
}

附加说明:使用公共字段测试状态是邪恶的。考虑可重复性,维护,以不同顺序运行测试。您应该在测试中移动bool methodFired。我还假设有一种更好的技术来测试它(但我不确定是哪一种)。

答案 2 :(得分:2)

如果是&#34;火灾和忘记&#34;您不需要从Task方法返回FireAndForget,因为来电者可以获取Task并取消它(严格来说,来电者会#34;记住&#34 34;电话)。

如果要从许多不从公共ThreadingService继承的服务调用此方法,可以通过接口实现扩展方法。

public interface IFireAndForget 
{
     // no member needed.
}

public static class FireAndForgetExtensions 
{
    public static void FireAndForget(this IFireAndForget obj, Action action) 
    {
         // pass the action, not a new lambda
         Task.Factory.StartNew(action);
    }
}


// using
public class ThreadingService : IFireAndForget 
{

}

另请注意,在您的方法中,您必须将action传递给传递lambda并返回StartNew参数的action方法。

答案 3 :(得分:1)

测试线程代码很难。 根据时间进行测试是一个坏主意,它们可能变得不确定,您可能会在构建服务器上观察到不稳定的行为。想象一下有时会通过的测试,有时却没有!

您的代码有错误,因为您实际上并未调用该操作。

但请考虑这种变化:

[Test]
[TimeOut(5000)]
public void CanFireAndForgetWithThreadingService()
{
   var service = new ThreadingService();
   ManualResetEvent mre = new ManualRestEvent(bool); // I never remember what is the default...

   service.FireAndForget(() => mre.Set() /*will release the test asynchroneously*/);
   mre.WaitOne(); // blocks, will timeout if FireAndForget does not fire the action.
}

是的,我们仍在使用计时。但是测试只有在代码中断时才会发生超时! 在所有其他情况下,测试是绝对可预测的,并且执行时间非常短,无需等待和祈祷时间问题不会发生; - )