aierda 2023-07-14 19:24 采纳率: 72.1%
浏览 59
已结题

ManualResetEvent.set()不生效的问题

问题描述:项目中有一个数据同步的函数,每隔2分钟被调用。
为了减少性能消耗,始终有两个线程去执行,线程之间通过ManualResetEvent
去传达信号. 先执行生产者线程,如果满足条件,通过ManualResetEvent.set
向消费者发出信号,消费者开始执行,执行完成后调用ManualResetEvent.WaitOne()
进行阻塞。
但问题是:第二次调用同步函数,却无法通过ManualResetEvent.set去唤醒线程。
详细的代码如下:

private static ManualResetEvent _mrePullData = new ManualResetEvent(false);//false初始化状态为无信号,将使WaitOne阻塞
private static Thread _producterThread = null; //生产者线程
private static Thread _costerThread = null;    //消费者线程

public ApiResults PullDataFromCenterAMS()  //这个函数每隔两分钟被调用
{
    ApiResults result = new ApiResults();
    result.message = "success";
    _logger.LogInformation("调用PullDataFromCenterAMS");
    try
    {
        if (_producterThread == null) //第一次调用,创建线程
        {
            _producterThread = new Thread(new ThreadStart(Product));
            _producterThread.Name = "Product";
            _producterThread.Start();
            _costerThread = new Thread(new ThreadStart(Cost));
            _costerThread.Name = "Cost";
            _costerThread.Start();
        }
        else //第二次执行时,不再创建线程
        {
            _mrePullData.Set();  //但是这里却没有办法唤醒生产者线程,也就是说无法调用Product()
        }
        return result;
    }
    catch (Exception ex)
    {
        result.message = "faile:" + ex.Message;
        return result;
    }
}
/// <summary>
/// 生产线
/// </summary>
private void Product()
{
    /*判断libraryCode是否有效,需要同步的数据是否存在,符合条件才打开信号*/
    string libraryCode = Appsettings.app(new string[] { "App", "LibraryCode" });
    _libraryModel = DAOControls.DAOControlsInit().GetLibraryByCode(libraryCode);
    if (_libraryModel == null)
    {
        _logger.LogInformation("图书馆代码无效,找不到对应的图书馆");
        _mrePullData.WaitOne(); //没有同步对象,即无信号,则会阻塞
    }
    else
    {
        _libraryName = _libraryModel.LibraryCName;
        _mrePullData.Set();//表示有信号了,通知WaitOne不再阻塞
        Thread.Sleep(100);
    }
}
/// <summary>
/// 消费线
/// </summary>
private async void Cost()
{
    /*
    通过http请求去同步数据,并将同步结果进行日志输出,如果遇到同步错误或者同步完成,将调用_mrePullData.WaitOne()进行阻塞
    */
    SyncResp syncResp = await _syncDataRequestServices.PullSysncDataOrder(_httpClientFactory, _syncDataURL, _libraryName, _lastSyncTime);
    if (syncResp.SyncResult.StartsWith("Fail")) //拉取数据过程中报错,此时输出日志,中断同步
    {
        _logger.LogInformation(syncResp.SyncResult);
        _mrePullData.WaitOne();
        return;
    }
    else
    {
        if (syncResp.SyncResult.StartsWith("TotalSyncFinished")) //所有的表都已同步完成,此时输出日志,中断同步
        {
            _logger.LogInformation(syncResp.SyncResult);
            _mrePullData.WaitOne();
            return;
        }
        else
        {
            if (syncResp.SyncResult.StartsWith("TableSyncFinished")) //某个表同步完成,此时输出日志,继续同步其它表
            {
                _logger.LogInformation(syncResp.SyncResult);
            }
            else
            {
                List<Audit> audits = syncResp.SyncResult.FromJsonString<List<Audit>>();
                string msg = await UpdateDataToDB(audits);
                if (msg == "success")
                {
                    _logger.LogInformation("此页数据成功同步到DB,继续同步");
                }
                else
                {
                    _logger.LogInformation("数据同步至DB时报错:" + msg + "请修复问题,同步中断");
                    _mrePullData.WaitOne();
                    return;
                }
            }
            ExecuteSyncOrder(); //继续同步
        }
    }
}

提出问题:ManualResetEvent.set()失效的原因是什么,有什么解决办法呢, 或者是否有其它实现方案
恳请大家指点,不胜感激,期待,谢谢!

  • 写回答

8条回答 默认 最新

  • 技术宅program 2023-07-14 21:34
    关注

    你的生产者线程和消费者线程都是异步的,可能存在竞争条件,导致ManualResetEvent的状态不一致,还有你的生产者线程在调用mre->Set()后没有调用mre->WaitOne(),你的消费者线程在调用mre->WaitOne()后没有调用mre->Reset(),而是继续执行

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(7条)

报告相同问题?

问题事件

  • 系统已结题 7月23日
  • 已采纳回答 7月15日
  • 创建了问题 7月14日