C# 可以在并发调用时等待损坏的值

C# 可以在并发调用时等待损坏的值,c#,.net,concurrency,task-parallel-library,async-await,C#,.net,Concurrency,Task Parallel Library,Async Await,简言之,我有这样的想法: class MyClass { double SomeProperty {get; private set;} public async Task SomeMethod() { SomeProperty = await someService.SomeAsyncMethod(); } } 如果我同时调用SomeMethod()多次,那么SomeProperty是否会损坏 如果我同时多次调用SomeMethod(),So

简言之,我有这样的想法:

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

    public async Task SomeMethod()
    {
        SomeProperty = await someService.SomeAsyncMethod();
    }
}
如果我同时调用
SomeMethod()
多次,那么
SomeProperty
是否会损坏

如果我同时多次调用SomeMethod(),SomeProperty是否会损坏

可能是的,但这取决于很多因素

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

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

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

MyClass()       // in constructor
{
    backing = someService.SomeAsyncMethod(); // Note, no await here!
}
任务备份;
double SomeProperty{get{return backing.Result;}
MyClass()//在构造函数中
{
backing=someService.SomeAsyncMethod();//注意,这里没有等待!
}
否则,如果您需要多个调用,那么显式的
锁或其他同步机制可能是最好的方法。

是的,可能是

这与
asyncwait
没有真正的关系。您只需通过多个线程为属性设置一个值。该值可能会损坏,您应该使用一些同步构造(即
):

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

但是,如果此方法在GUI线程中运行(或在任何其他情况下使用单线程的同步上下文),则不会发生损坏,因为只有一个线程处理该属性。

由于您使用的是
wait
,因此会调用该方法,从
someService.somesynchmethod()
获取
任务
,然后产生控制,直到所述
任务
完成


当该任务完成时,控件返回(通常在调用任务的同一线程上,尽管这取决于@Reed指出的
SynchronizationContext
),然后调用
SomeProperty
set
函数。此时,所述调用可能来自多个线程,或者来自启动任务的线程以外的线程,从而导致在没有
lock
s的情况下出现不可预测的get/set行为。

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

读取和写入双精度不是线程安全的,因此最好在它们周围使用锁。使用
Interlocked.COmpareExchange
将正确写入变量,但您也需要以线程安全的方式读取变量,因此基本上需要一个锁。请参阅Eric Lippert。

如果您只是从其他地方检索您的
double
,情况将完全相同。是的,如果您在同一MyClass实例上同时多次调用
SomeMethod()
,它可能会损坏。@zerkms,这样它就可以正确损坏了吗?@Sniffer:没错
await
没有在此处添加任何新的语义。您提出的问题表明,您正在考虑做一些危险的事情,这可能是架构级别的错误决策造成的。设计应用程序时,不要跨线程共享内存。想象一个线程是一个进程,你不能很容易地共享内存;那么您将如何设计您的应用程序呢?尽管这是一个不同的问题。在这种情况下,解决问题的最佳方法是什么?@Sniffer您希望实现什么?假设有两个并发线程分配,哪一个必须赢?在64位机器上写一个double保证是原子的,不是吗?我记得有人在一次类似的会议上提到过question@zerkms我只想确保原子写入
SomeProperty
,因为我知道它可能会被破坏。即使它是原子的,你也需要某种屏障。仅仅更新值可能会导致不同线程的视图不一致。我喜欢您的解释,因为起初我认为
SomeProperty
将在一个continuation中设置。谢谢。控件返回不是基于线程,而是基于当前SynchronizationContext。好的,通常控件返回到调用任务的线程。我会合并这个。
public async Task SomeMethod()
{
    var temp = await someService.SomeAsyncMethod();
    lock (_lock)
    {
        SomeProperty = temp;
    }
}
public async Task SomeMethod()
{
    Interlocked.Exchange(ref _backingField, await someService.SomeAsyncMethod());
}