2

我有一个与外部 .exe 对话的课程。该类有一堆类似的方法;他们调用 .exe 的函数,等待响应,然后返回 true 或 false。

响应以更改此类字段值的事件的形式出现。

简化代码:

class Manager
{
    private static bool connected = false;

    public static bool Connect()
    {
        runtime.Connect();

        int secondsWaited = 0;

        while (!connected)
        {
            Thread.Sleep(1000);

            if (secondsWaited++ == 10)
            {
                return false;
            }
        }
        return true;
    }
}

其他方法使用相同的 call-wait-loop-return 结构。

我的目标是制作一个等待我的方法来执行此操作,如下所示:

private static bool WaitReferenceEqualsValue<T>(ref T reference, T value)
{
    int secondsWaited = 0;

    while (!reference.Equals(value))
    {
        Thread.Sleep(1000);

        if (secondsWaited++ == 10)
        {
            return false;
        }
    }
    return true;
}

然后每种方法都会做:

runtime.DoSomething();

return WaitReferenceEqualsValue<someType>(ref someField, someSuccessfulValue);

但是,当我用此方法调用替换等待循环时,“已连接”字段,即使作为引用传入,也始终保持不变。

知道这里发生了什么,以及如何获得所需的功能吗?

提前致谢。

编辑:

public static bool Connect()
{
    ...
    runtime.Connect();

    // this code works
    /*int secondsWaited = 0;

    while (connected != true)
    {
        Thread.Sleep(1000);

        if (secondsWaited++ == 10)
        {
            return false;
        }
    }*/

    // this somehow blocks OnConnect from firing, so connected never gets set to true
    lock (typeof(SkypeKitManager))
    {
        WaitReferenceEqualsValue<bool>(ref connected, true);
    }
    ...
}

连接:

private static void OnConnect(object sender, Events.OnConnectArgs e)
{
    if (e != null && e.success)
    {
        lock (typeof(Manager))
        {
            connected = true;
        }
    }
}
4

2 回答 2

3

尽管您从多个线程访问它并且其中一个正在写入,但您没有对该字段进行任何同步。这是一场比赛(也不例外!这一场比赛,即使看起来很安全。它并不安全。)。

可能是 JIT 注册了它,这是一种常见的优化。它永远不会从内存中读取,总是从寄存器中读取。添加同步(例如锁,或 Interlocked 或 Volatile 方法)。

您对 ref 的使用是正确的。

于 2013-03-07T19:59:11.187 回答
2

您的代码的问题本质上是编译器优化。出于优化目的的编译器(或 jit)必须采用几乎单线程的视图。然后编译器/jit 会注意到你根本没有接触reference你的代码,因此它可以将比较移到循环之外。这样做是免费的,因为您基本上创建了一个竞争条件(没有同步/原子访问)。

修复它可能涉及使用同步机制或将说明volatile符添加到reference,从而告诉编译器/jit,可以从方法外部更改变量。

于 2013-03-07T20:01:20.177 回答