简单地说,我有这样的事情:
class MyClass
{
double SomeProperty {get; private set;}
public async Task SomeMethod()
{
SomeProperty = await someService.SomeAsyncMethod();
}
}
如果我同时多次调用SomeProperty
,SomeMethod()
是否会损坏?
答案 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)
async
和await
不添加任何并发保护。如果在没有async
和await
的情况下它可能会损坏,那么它们也会被破坏。
答案 3 :(得分:1)
是
由于您使用的是await
,因此会调用该方法,从Task
获取someService.SomeAsyncMethod()
,然后产生控制权,直到Task
完成。
当该任务完成时,控制返回(通常在调用任务的同一个线程上,尽管这取决于SynchronizationContext
,如@Reed所指出的),然后调用set
函数{ {1}}。此时,所述调用可能来自多个线程,或来自启动任务的线程之外的线程,导致不可预测的获取/设置行为而没有SomeProperty
。
答案 4 :(得分:1)
读取和写入double不是线程安全的,因此最好使用锁定。使用Interlocked.COmpareExchange
将正确编写变量,但您需要以线程安全的方式读取它,因此基本上您需要一个锁。请参阅Eric Lippert的this answer。