Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/http/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使闭包捕获的变量可变_C#_Closures_Volatile - Fatal编程技术网

C# 使闭包捕获的变量可变

C# 使闭包捕获的变量可变,c#,closures,volatile,C#,Closures,Volatile,闭包捕获的变量如何与不同的线程交互?在下面的示例代码中,我希望将totalEvents声明为volatile,但C#不允许这样做 (是的,我知道这是错误的代码,这只是一个示例) private void WaitFor10Events() { volatile int totalEvents=0;//错误CS0106: _someEventGenerator.SomeEvent+=(s,e)=>totalEvents++; while(totalEvents联锁的增量(参考totalEvents

闭包捕获的变量如何与不同的线程交互?在下面的示例代码中,我希望将totalEvents声明为volatile,但C#不允许这样做

(是的,我知道这是错误的代码,这只是一个示例)

private void WaitFor10Events()
{
volatile int totalEvents=0;//错误CS0106:
_someEventGenerator.SomeEvent+=(s,e)=>totalEvents++;
while(totalEvents<10)
睡眠(100);
}
编辑:人们似乎忽略了我问题的要点。我知道我不能在本地变量上使用
volatile
。我还知道示例代码是错误的,可以用其他方式实现,因此我的“错误代码”免责声明。这只是为了说明问题


无论如何,似乎没有办法将易失性语义强制到捕获的局部变量上,所以我将实现一种不同的方法。不过,谢谢你的回答,我还是学到了一些有用的东西

不能声明局部变量
volatile
。此外,还有更好的方法来实现你的目标。。。改用。这将比你的投票/睡眠方法更有效

using(CountdownEvent cde = new CountdownEvent(10))
{
  _someEventGenerator.SomeEvent += (s, e) => cde.Signal();
  cde.Wait();
}

将局部变量标记为volatile是无效的。闭包可以捕获易失性字段,以下是完全合法的:

volatile int totalEvents = 0;
private void WaitFor10Events()
{
   _someEventGenerator.SomeEvent += (s, e) => totalEvents++;
   ...
}
查看关于volatile关键字的信息

作为旁白,您可以考虑使用RealEnter事件(,)、监视器类(和方法)或A来进行线程休眠直到事件被提升,这比在循环中休眠更有效。 更新

根据对问题的编辑,获取线程安全语义的一个简单方法是使用。以这种方式重新编写示例(尽管如其他答案所述,有更好的方法编写此示例):

private void WaitFor10Events()
{
long totalEvents=0;
_someEventGenerator.SomeEvent+=(s,e)=>联锁的增量(参考totalEvents);
while(联锁读取(参考totalEvents)<10)
{
睡眠(100);
}
}

如果并行触发事件,则此操作将不起作用。不幸的是,n++在.NET中并不是一个原子操作,所以你不能期望执行n++10次的多个线程实际增加n 10,它可以增加更少。这里有一个小程序可以证明这一点(在这个过程中,确保在并行使用时正确处理闭包):

类程序
{
静态易失性int_outer=0;
静态void Main(字符串[]参数)
{
int内部=0;
动作act_outer1=()=>_outer++;//联锁的.增量(ref _outer);
Action act_inner1=()=>inner++;//Interlocked.Increment(ref-inner);
动作组合=(i)=>{act_outer1();act_inner1();};
WriteLine(“无外部={0},内部={1}”,_外部,内部);
平行。对于(0,20000000,组合);
WriteLine(“一次外部={0},内部={1}”,_外部,内部);
Console.ReadKey();
}
}

Interlocked.Increment变量按预期工作。

闭包捕获的局部变量将传递到编译器生成的另一个类,这样做时不会放松“局部变量不能是易失性的”规则,即使局部变量“真的”最终作为实例字段。最好是手动构造闭包类(“函数对象”)或使用一些IL操作工具,例如ILDASM。

Volatile。编写拯救:

private void WaitFor10Events()
{
     int totalEvents = 0; 

     _someEventGenerator.SomeEvent += (s, e) => Volatile.Write(ref totalEvents, totalEvents+1);

     while(totalEvents < 10)
        Thread.Sleep(100);
}
private void WaitFor10Events()
{
int totalEvents=0;
_someEventGenerator.SomeEvent+=(s,e)=>Volatile.Write(参考totalEvents,totalEvents+1);
while(totalEvents<10)
睡眠(100);
}

也就是说,对于这种特殊情况,我仍然会使用
Interlocked.Increment

声明变量
volatile
,您希望得到什么?在大多数情况下,
volatile
的用户期望它做任何事情,而不是它实际做的事情。@Mr.loission-OP知道这一点-这就是为什么他/她说“我想声明totalEvents为volatile,但C#不允许”@Mr.loission-我相信OP知道为什么-这是一个有趣的问题。我认为OP试图理解的是:在类级别,您可以对字段应用
volatile
,以确保它可以由多个线程安全地读写(所有线程都看到最新的值),因为没有应用某些编译器优化。在上面的例子中,OP使用闭包模拟类似的情况<在该示例中,代码>totalEvents可由多个线程修改。问题是“你能在这种情况下得到同样的行为吗?如果是,如何得到?”@GazTheDestroyer-我知道你能-我说的是“OP确实理解这一点,并且提出了一个很好的问题”@Robleine:哦,对不起,Rob,把我的用户名弄错了。它没有回答问题。
private void WaitFor10Events()
{
   long totalEvents = 0;
   _someEventGenerator.SomeEvent += (s, e) => Interlocked.Increment(ref totalEvents);

   while(Interlocked.Read(ref totalEvents) < 10)
   {
     Thread.Sleep(100);
   }
}
class Program
{
    static volatile int _outer = 0;
    static void Main(string[] args)
    {
        int inner = 0;

        Action act_outer1 = () => _outer++; // Interlocked.Increment(ref _outer);
        Action act_inner1 = () => inner++;  // Interlocked.Increment(ref inner);
        Action<int> combined = (i) => { act_outer1(); act_inner1(); };

        Console.WriteLine("None outer={0}, inner={1}", _outer, inner);
        Parallel.For(0, 20000000, combined);
        Console.WriteLine("Once outer={0}, inner={1}", _outer, inner);
        Console.ReadKey();
    }
}
private void WaitFor10Events()
{
     int totalEvents = 0; 

     _someEventGenerator.SomeEvent += (s, e) => Volatile.Write(ref totalEvents, totalEvents+1);

     while(totalEvents < 10)
        Thread.Sleep(100);
}