You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#实现PLC事件请求串行处理:解决并发执行问题

PLC请求串行处理改造

我正在与外部PLC设备通信,PLC主动发起数据请求。我通过监听PLC变量值变化(如NEEDNEWPOSITION或NEEDNEWBARVALUES)来响应请求,但目前偶尔会出现两个请求同时执行的情况(推测是不同请求的处理耗时差异导致)。希望修改代码实现请求的串行处理,已知async/await、Task这类异步方案,但认为实现成本过高,现有代码如下:

private void PLCValueChanged(object sender, EventArgs e)
{
    bool xDisplayValue = false;
    PLCVar plcvariable = (PLCVar)sender;
    string VarName = plcvariable.DisplayName;

    switch (VarName)
    {
        case "NEEDNEWPOSITION": //Writing required position to PLC
        if (Convert.ToBoolean(plcvariable.Value))
        {
            SearchNewPosition();
            OPCclient.SendVarToPLC(OPCclient.SendPlcAllBarsFinished, "FALSE");
            OPCclient.SendVarToPLC(OPCclient.SendPLCAllMagnetsFinished, "FALSE");

            MagnetsInArea = GetMagnetsInWorkArea();
            SymbolsInArea = GetSymbolsInWorkArea();
            BarsInArea = GetBarsInWorkArea();
        }
        else
        {
            OPCclient.SendVarToPLC(OPCclient.SendPLCNewPositionIsSend, "FALSE");
        }
        break;

        case "NEEDNEWBARVALUES": //Writing Bar Values to PLC
        if (Convert.ToBoolean(plcvariable.Value))
        {
            OPCclient.SendVarToPLC(OPCclient.SendPLCBarStrippedOK, "FALSE");
            OPCclient.SendVarToPLC(OPCclient.SendPLCBarMagnetsOK, "FALSE");
            OPCclient.SendVarToPLC(OPCclient.SendPLCAllBarMagnetsLoose, "FALSE");

            SetFirstBarValues();

            OffsetsCalculated = false;

            StartVisualisation?.Invoke(this, null); //%M59
        }
        else //if (!Convert.ToBoolean(plcvariable.Value))
        {
            OPCclient.SendVarToPLC(OPCclient.SendPLCBarDataIsSend, "FALSE");
        }
        break;
    }
}

低成本串行处理方案:队列+后台线程

不需要引入复杂的异步框架,用请求队列+后台线程即可实现串行处理,改动量小:

  1. 定义请求枚举和队列:
// 标识请求类型
private enum PLCRequestType
{
    NeedNewPosition_True,
    NeedNewPosition_False,
    NeedNewBarValues_True,
    NeedNewBarValues_False
}

// 线程安全队列存储请求
private readonly ConcurrentQueue<PLCRequestType> _plcRequestQueue = new ConcurrentQueue<PLCRequestType>();
// 后台处理线程
private Thread _requestProcessingThread;
// 标识线程是否运行
private bool _isProcessingThreadRunning;
  1. 初始化后台线程(在类的构造函数或初始化方法中):
public YourClassName() // 替换为你的类名
{
    _isProcessingThreadRunning = true;
    _requestProcessingThread = new Thread(ProcessPLCRequests)
    {
        IsBackground = true, // 后台线程随程序退出自动终止
        Priority = ThreadPriority.Normal
    };
    _requestProcessingThread.Start();
}
  1. 实现队列处理逻辑:
private void ProcessPLCRequests()
{
    while (_isProcessingThreadRunning)
    {
        if (_plcRequestQueue.TryDequeue(out var requestType))
        {
            HandlePLCRequest(requestType);
        }
        else
        {
            // 队列为空时短暂休眠,避免占用过多CPU
            Thread.Sleep(10);
        }
    }
}

// 原事件中的处理逻辑移到这里
private void HandlePLCRequest(PLCRequestType requestType)
{
    switch (requestType)
    {
        case PLCRequestType.NeedNewPosition_True:
            SearchNewPosition();
            OPCclient.SendVarToPLC(OPCclient.SendPlcAllBarsFinished, "FALSE");
            OPCclient.SendVarToPLC(OPCclient.SendPLCAllMagnetsFinished, "FALSE");

            MagnetsInArea = GetMagnetsInWorkArea();
            SymbolsInArea = GetSymbolsInWorkArea();
            BarsInArea = GetBarsInWorkArea();
            break;
        case PLCRequestType.NeedNewPosition_False:
            OPCclient.SendVarToPLC(OPCclient.SendPLCNewPositionIsSend, "FALSE");
            break;
        case PLCRequestType.NeedNewBarValues_True:
            OPCclient.SendVarToPLC(OPCclient.SendPLCBarStrippedOK, "FALSE");
            OPCclient.SendVarToPLC(OPCclient.SendPLCBarMagnetsOK, "FALSE");
            OPCclient.SendVarToPLC(OPCclient.SendPLCAllBarMagnetsLoose, "FALSE");

            SetFirstBarValues();
            OffsetsCalculated = false;
            StartVisualisation?.Invoke(this, null); //%M59
            break;
        case PLCRequestType.NeedNewBarValues_False:
            OPCclient.SendVarToPLC(OPCclient.SendPLCBarDataIsSend, "FALSE");
            break;
    }
}
  1. 修改原事件处理方法,仅负责入队请求:
private void PLCValueChanged(object sender, EventArgs e)
{
    PLCVar plcvariable = (PLCVar)sender;
    string VarName = plcvariable.DisplayName;
    bool isValueTrue = Convert.ToBoolean(plcvariable.Value);

    switch (VarName)
    {
        case "NEEDNEWPOSITION":
            _plcRequestQueue.Enqueue(isValueTrue ? PLCRequestType.NeedNewPosition_True : PLCRequestType.NeedNewPosition_False);
            break;
        case "NEEDNEWBARVALUES":
            _plcRequestQueue.Enqueue(isValueTrue ? PLCRequestType.NeedNewBarValues_True : PLCRequestType.NeedNewBarValues_False);
            break;
    }
}
  1. 可选:在类销毁时停止后台线程(如Dispose方法):
public void Dispose()
{
    _isProcessingThreadRunning = false;
    _requestProcessingThread?.Join(1000); // 等待线程终止,超时则强制结束
}

方案优势

  • 改动量小:原处理逻辑几乎无需修改,仅需迁移到单独方法
  • 无异步框架依赖:不需要重构为async/await模式,学习成本低
  • 严格串行:后台线程逐个处理队列请求,彻底避免并发执行问题

内容的提问来源于stack exchange,提问作者Bart

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 21:48:28