2

タスク並列ライブラリを使用して、ファイアアンドフォーゲット機能を実装しようとしています。へのインライン呼び出しを使用すると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

4 に答える 4

3

アクションを実行しているのではなく、単にそれを返しているだけです。

試す:

return Task.Factory.StartNew(() => action());
于 2012-09-02T18:56:19.250 に答える
2

でアクションを呼び出さなかったThreadingService

コードは次のようになります

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

追記:パブリックフィールドで状態をテストすることは悪です。再現性、メンテナンス、テストの実行をさまざまな順序で検討してください。bool methodFiredテスト内に移動する必要があります。また、これをテストするためのより良い手法があると思います(ただし、どちらかはわかりません)。

于 2012-09-02T18:52:17.250 に答える
2

が「ファイアアンドフォーゲット」の場合、呼び出し元がそれを取得してキャンセルできるため、メソッドTaskからを返す必要はありません(厳密に言えば、呼び出し元は呼び出しを「記憶」します)。FireAndForgetTask

コモンから継承しない多くのサービスからこのメソッドを呼び出したい場合は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したメソッドにを渡す必要があることに注意してください。StartNewaction

于 2012-09-02T18:57:50.063 に答える
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.
}

はい、まだタイミングを使用しています。ただし、テストのタイムアウトは、コードが壊れた場合にのみ発生します。他のすべてのシナリオでは、テストは完全に予測可能であり、実行に非常に短い時間がかかり、タイミングの問題が発生しないように待ったり祈ったりする必要はありません;-)

于 2012-09-02T18:58:43.247 に答える