C#中的Promise等效项


75

在Scala中,有一个Promise类可用于手动完成Future。我正在寻找C#中的替代方法。

我正在编写一个测试,并且希望它看起来像这样:

// var MyResult has a field `Header`
var promise = new Promise<MyResult>;

handlerMyEventsWithHandler( msg =>
    promise.Complete(msg);
);

// Wait for 2 seconds
var myResult = promise.Future.Await(2000);

Assert.Equals("my header", myResult.Header);

我知道这可能不是C#的正确模式,但是即使模式有所不同,我也无法找出实现相同目标的合理方法。

编辑:请注意,async/await在这里没有帮助,因为我没有等待的任务!我只有访问将在另一个线程上运行的处理程序的权限。


3
我想你在找Task<T>
戴文·崔顿

Answers:


110

在C#中:

  • Task<T>是一个未来(或Task一个单位返回的未来)。
  • TaskCompletionSource<T> 是一个承诺。

因此,您的代码将这样转换:

// var promise = new Promise<MyResult>;
var promise = new TaskCompletionSource<MyResult>();

// handlerMyEventsWithHandler(msg => promise.Complete(msg););
handlerMyEventsWithHandler(msg => promise.TrySetResult(msg));

// var myResult = promise.Future.Await(2000);
var completed = await Task.WhenAny(promise.Task, Task.Delay(2000));
if (completed == promise.Task)
  ; // Do something on timeout
var myResult = await completed;

Assert.Equals("my header", myResult.Header);

“定时异步等待”有点尴尬,但是在现实世界的代码中也很少见。对于单元测试,我只需要进行常规的异步等待:

var promise = new TaskCompletionSource<MyResult>();

handlerMyEventsWithHandler(msg => promise.TrySetResult(msg));

var myResult = await promise.Task;

Assert.Equals("my header", myResult.Header);

有一种使用取消令牌来实现超时的更好的方法。参见stackoverflow.com/q/23476576/1288449
Steven Liekens

7
@StevenLiekens:我同意超时通常可以更好地表示为取消令牌;但是,最适合使用超时来取消操作的情况。在这种情况下,我们谈论的是取消wait而不是operation,并且取消令牌在这种情况下更加尴尬。
Stephen Cleary

18

如果没有第三方库,则大致的C#等效项为:

// var MyResult has a field `Header`
var promise = new TaskCompletionSource<MyResult>();

handlerMyEventsWithHandler(msg =>
  promise.SetResult(msg)
);

// Wait for 2 seconds
if (promise.Task.Wait(2000))
{
  var myResult = promise.Task.Result;
  Debug.Assert("my header" == myResult.Header);
}

请注意,通常最好将await/async设置为尽可能高的级别。在某些情况下,访问ResultaTask或usingWait导致死锁


@Stephen的答案是纯C#。这里有什么不同。
Sahib Khan

1
@SahibKhan,1.我在他之前发布过,2.在这之前也是纯C#,尽管可能不再是最佳实践。
黑暗猎鹰


3

这是守旧的承诺方式。
那时我相信它被称为同步:)

MyResult result = null;
var are = new AutoResetEvent(false);

handlerMyEventsWithHandler( 
    msg => {result = msg; are.Set();}
);

// Wait for 2 seconds
if(!are.WaitOne(2000)) {/* handle timeout... */}

Assert.Equals("my header", myResult.Header);

只是为了完整性-大量评论。
我同意Stephen Cleary的回答

但是,如果您要围绕一些旧代码构建外观,则可以将其用于将旧API封装在Task中,例如:

public Task<MyResult> GetResultAsync() {
    MyResult result = null;
    var are = new AutoResetEvent(false);
    handlerMyEventsWithHandler(msg => {
        result = msg;
        are.Set();
    });
    are.WaitOne();
    return Task.FromResult(result);
}


-1

您可以从Nuget下载future(https://www.nuget.org/packages/Future/)软件包,并且可以按以下方式使用

        Promise<int> promise = new Promise<int>();
        new Task(() =>
        {
            Thread.Sleep(100);
            promise.Set(20);

        }).Start();
        int result=promise.Get();

按照示例,您可以创建一个promise对象并执行get获取结果,get将等待直到结果出现在该对象上。如上例所示,您从另一个线程进行了设置。

该软件包提供以下两个类

  1. 承诺:无限期等待结果

  2. TimedPromise:仅在指定时间之前等待结果。如果结果在时间内不可用,则会引发超时异常

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.