可以等待同时调用时损坏值

时间:2014-12-19 00:27:52

标签: c# .net concurrency task-parallel-library async-await

简单地说,我有这样的事情:

class MyClass
{
    double SomeProperty {get; private set;}

    public async Task SomeMethod()
    {
        SomeProperty = await someService.SomeAsyncMethod();
    }
}

如果我同时多次调用SomePropertySomeMethod()是否会损坏?

5 个答案:

答案 0 :(得分:6)

是的,可能。

这与async-await无关。您只需通过多个线程为属性设置值。该值可能会被破坏,您应该使用一些同步构造(即lock):

public async Task SomeMethod()
{
    var temp = await someService.SomeAsyncMethod();
    lock (_lock)
    {
        SomeProperty = temp;
    }
}

Interlocked.Exchange(使用属性的支持字段):

public async Task SomeMethod()
{
    Interlocked.Exchange(ref _backingField, await someService.SomeAsyncMethod());
}

但是,如果此方法在GUI线程(或任何其他具有单线程SynchronizationContext的情况下)运行,则不存在损坏的可能性,因为只有一个线程处理该属性。

答案 1 :(得分:3)

  

如果我多次同时调用SomeMethod(),SomeProperty会被破坏吗?

可能,是的,虽然这取决于很多因素。

写一个double并不能保证是一个原子操作。由于您同时调用此方法,因此可能会同时写入值,这可能会导致未定义的行为。

有各种机制来处理这个问题。通常,您只需要调用一次服务,在这种情况下,您可以直接存储任务:

Task<double> backing;
double SomeProperty { get { return backing.Result; } }

MyClass()       // in constructor
{
    backing = someService.SomeAsyncMethod(); // Note, no await here!
}

否则,如果您需要多次调用,则显式lock或其他同步机制可能是最佳方法。

答案 2 :(得分:2)

asyncawait不添加任何并发保护。如果在没有asyncawait的情况下它可能会损坏,那么它们也会被破坏。

答案 3 :(得分:1)

由于您使用的是await,因此会调用该方法,从Task获取someService.SomeAsyncMethod(),然后产生控制权,直到Task完成。

当该任务完成时,控制返回(通常在调用任务的同一个线程上,尽管这取决于SynchronizationContext,如@Reed所指出的),然后调用set函数{ {1}}。此时,所述调用可能来自多个线程,或来自启动任务的线程之外的线程,导致不可预测的获取/设置行为而没有SomeProperty

答案 4 :(得分:1)

读取和写入double不是线程安全的,因此最好使用锁定。使用Interlocked.COmpareExchange将正确编写变量,但您需要以线程安全的方式读取它,因此基本上您需要一个锁。请参阅Eric Lippert的this answer