自动重置事件的这种用法是否会导致所描述的问题

本文关键字:问题 描述 是否 事件 用法 | 更新日期: 2023-09-27 18:30:04

我不习惯使用AutoResetEvent,并且想知道以下情况是如何发生的。

我们有一个日志文件,它始终在两台不同的机器上记录不同的加载操作顺序:

机器 A(出现问题(

加载模块 A加载模块 B加载外壳。显示窗口。等到服务加载和初始化。装载模块 C正在启动应用程序..

机器 B(普通日志文件(

加载模块 A加载模块 B装载模块 C正在启动应用程序..加载外壳。显示窗口。正在初始化菜单元素...

第一台计算机从不加载菜单元素。

代码是这样的:

public class SplashScreen : Form
{
    // SplashScreen.Run method which signals the ProgressCompleted event
    private void Run()
    {
        // some code
        MySingleton.Instance.ExecutionCompleted.WaitOne();
        MySingleton.Instance.Completed = true;
        MySingleton.Instance.ProgressCompleted.Set();
        // more code
    }
}
public class ShellApplication : FormShellApplication<WorkItem, ShellForm>
{    
    // ShellApplication.Start override
    protected override void Start()
    {
        MySingleton.Instance.ProgressCompleted.WaitOne();
        WriteToLog("Starting Application ..");
        base.Start();
    }
}
public class ShellForm : Form
{    
    // Background worker that runs from ShellForm.Load
    private void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        // check to ensure services & ui components are loaded & initialized 
        if (!MySingleton.Instance.Completed)
        {
            WriteToLog("Waiting Until Services are Loaded & Initialized.");
            MySingleton.Instance.ProgressCompleted.WaitOne();
        }
    }
    // Background worker’s RunWorkerCompleted that runs from ShellForm.Load
    private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        // Some Code
        WriteToLog("Initializing Menu Elements ...");
    }
}

单例使用AutoResetEvent表示ExecutionCompletedProgressCompleted,并使用带有同步锁的布尔值表示Completed标志

public class MySingleton
{
    private object _sync = new object();
    private static AutoResetEvent _executionCompleted = new AutoResetEvent(false);
    private static AutoResetEvent _progressCompleted = new AutoResetEvent(false);
    private static bool _completed = false;
    public AutoResetEvent ProgressCompleted
    {
        get
        {
            lock (_sync)
            {
                return _progressCompleted;
            }
        }
    }
    public AutoResetEvent ExecutionCompleted
    {
        get 
        {
            lock (_sync)
            {
                return _executionCompleted;
            }
        }
    }
    public bool Completed
    {
        get
        {
            lock (_sync)
            {
                return _completed;
            }
        }
        set
        {
            lock (_sync)
            {
                _completed = value;
            }
        }
    }
}

据我所知,看起来在机器 A 上,后台工作线程在所有模块加载之前启动,并且ProgressCompleted.WaitOne()要么永远不会被击中,要么在 .Completed 标志为假时被击中。我没有足够的AutoResetEvent经验来知道代码中是否有任何内容会导致这种情况,例如对它的第二次.WaitOne调用。

我不认为这是与同步锁相关的计时问题,因为它每次都在机器 A 上发生,而我无法在机器 B(我的机器(上重现问题。

上面的代码中是否有任何与AutoResetEvent用法相关的内容可能导致此异常行为?我正在使用.Net 3.5。

自动重置事件的这种用法是否会导致所描述的问题

您可能需要

考虑使用 ManualResetEvent 而不是 AutoResetEvent

重置事件就像一个红绿灯。由于您用false初始化它,因此它以红灯开始。当您set它时,它会变成绿色。ManualResetEventAutoResetEvent之间的区别在于它在绿色上停留了多长时间......

ManualResetEvent将保持绿色,直到有人打电话给ResetAutoResetEvent会让一辆车进入并再次自动恢复到红灯。您设置了一次ProgressCompleted,但您等待了两次,第一次是在ShellApplication.Start,第二次是在worker_DoWork。这意味着第二次等待将永远等待。

它不会总是失败的原因是if (!MySingleton.Instance.Completed),如果初始屏幕代码比后台辅助角色快,则不要第二次等待重置事件。