Bart
Bart

Reputation: 767

Wait for method to be finished C#

I'm communication with an external device (PLC) and he requests data.

I have an event that checks when a value changes in my PLC (for example "NeedNewPosition" or "NeedNewBarValues")

I would like to change my code that it will handle them one by one. Sometimes it seems he's handling 2 of them at the same time. (probably since one takes longer than the other to finish) I've read something about async methods and wait/tasks etc, but that seems a lot of work for something this simple.

The code:

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;

Upvotes: 1

Views: 249

Answers (2)

JHBonarius
JHBonarius

Reputation: 11261

It sounds like you are looking for a Semaphore. Like the like/wiki says:

a semaphore is a variable or abstract data type used to control access to a common resource by multiple threads and avoid critical section problems in a concurrent system such as a multitasking operating system.

I.e. you can use the semaphore to "block" until a resource becomes available again.

You have multiple types of semaphores in C#, but the simplest to use is the SemaphoreSlim.

You can just define a static one for your singleton class instance

private static readonly SemaphoreSlim _semaphore = new(1, 1);

The 1,1 means: "1 is available, and there can only be 1".

Then in your code:

// take a semaphore, or wait until it is available
await _semaphore.WaitAsync(Timeout.Infinite, cancellationToken);
try
{
    [.. your work...]
}
finally
{
    // give the semaphore back
    _semaphore.Release();
}

Note, I'm using await here, because this means the thread becomes available for other tasks. It will also wait indefinitely until a semaphore is available. The way to stop this is the cancallationToken.

Upvotes: 1

Good Night Nerd Pride
Good Night Nerd Pride

Reputation: 8452

You could wait for the processing of the first event to be finished using an AutoResetEvent:

using System.Threading;
// ...

// declare lock as a static class member
private static AutoResetEvent barsInAreaLoaded = new AutoResetEvent(false);

private void PLCValueChanged(object sender, EventArgs e)
{
    // ...

    switch (VarName)
    {
        case "NEEDNEWPOSITION":
        if (Convert.ToBoolean(plcvariable.Value))
        {
            // ...
            BarsInArea = GetBarsInWorkArea();
            // signal waiting thread that bars are ready
            barsInAreaLoaded.Set();
        }
        // ...
        break;

        case "NEEDNEWBARVALUES":
        if (Convert.ToBoolean(plcvariable.Value))
        {
            // ...
            
            // block until bars are ready
            barsInAreaLoaded.WaitOne();
            SetFirstBarValues();
            
            // ...
        }
        // ...
        break;

Note that this will only work if you are sure that the processing of two corresponding NEEDNEWPOSITION and NEEDNEWBARVALUES messages overlap. If some of those messages actually pile up this won't solve your problem and you should consider implementing some kind of message queue/pipeline.

Upvotes: 0

Related Questions