FrougeurPro
FrougeurPro

Reputation: 59

C# - converting synchronous program into asynchronous program

I am currently rewriting my code to add asynchronous execution. However, I'm having trouble with adapting a group of methods.

I looked at a few questions like this one before asking but often time the problem was using Task.Run() instead of async/await.

=======Abstract=======

My program will be used to automate a test bench with multiple valves. As such I have written methods like open/closeValve() that open or close a valve to a certain threshold, increase/decreaseFlow() to select which valve to open or close and a regulate() method that calls increqse/decreaseFlow().

It's just recently that I decided, after reading about it, that adding asynchronous execution was a good idea to display data about the bench in real time. But my methods were all thought out to work in a synchronous way so I'm having trouble adapting them.

=======Code=======

For example here is the openValve() method

public int openValve(int limit)
    {
        if(_masterFlowrate >= _instructions - 0.1 && _masterFlowrate <= _instructions + 0.1)
        {
            //flowrate ~= instructions => no need to manipulate the valves
            return 1;
        }
        else
        {
            if(valveOpening < limit)
            {
                //flowrate < instructions & valve opened less than the limit
                // => opening valve untill the limit or untill flowrate = instructions
                valveOpening++;
 
                //Opening the valve by writing the value in a command computer via Modbus
                master.WriteSingleRegister(1, _mdbAdr, Convert.ToUInt16(ouvertureValve));
                return 0;
            }
            else
            {
                //flowrate < instructions & valve already opened beyond the limit
                // => not manipulating the valve
                return -1;
            }
        }

To adapt this method to asynchronous execution I changed the returned value type from int to Task<int> and changed return 1 to return Task.FromResult(1) for example.

Now the increaseFlow() method that directly uses the previous one looks like this (Vp, Vm and Vg are instances of the Valve class)

 public int increaseFlow()
    {
        int retVal = 0;
        switch(openingStep)
        {
            case 1:
                retVal = Vp.openValve(60);
                if(retVal > 0) openingStep = 4;
                else
                {
                    if(retVal < 0) openingStep = 2;
                }
                break;

            case 2:
                retVal = Vg.openValve();
                if(retVal > 0) openingStep = 4;
                else
                {
                    if(retVal < 0) openingStep = 3;
                }
                break;

            case 3:
                retVal = Vm.openValve();
                if(retVal > 0) openingStep = 4;
                else
                {
                    if(retVal < 0) openingStep = 4;
                }
                break;
        }

        return retVal;
    }

This is where I'm starting to have troubles. I've tried changing int retVal = 0; into var retVal = Task.FromResult(0) which doesn't give me any compiler error.

Then adding awaits keywords before each openValve() but this gives me the following :

Error   CS0029  Cannot implicitly convert type 'int' to 'System.Threading.Tasks.Task<int>'

And then the regulate() method calls increaseFlow().

=======Conclusion=======

That's where I began to think that maybe the code wasn't thought out to work asynchronously and that it needed to be rearranged. Is this really my problem or is there a solution that wouldn't require to completely redo the methods. I'm not saying that I wouldn't do it but I'm interested in knowing if the structure is wrong or if I'm just not using the right keywords/methods etc...

Upvotes: 0

Views: 530

Answers (2)

JonasH
JonasH

Reputation: 36361

A possible reason is the difference between async Task<T> and Task<T>. I.e.

public async Task<int> MyMethod(){
    return 42; // Task creation is implicit
}

And

public Task<int> MyMethod(){
    return Task.FromResult(42); // Task creation is explicit
}

The much larger question is why you are rewriting your system with async methods. There are typically a few main reasons:

  1. You are using some kind of IO, disk, network etc. Asynchronous methods helps avoid the blocking the thread when using these methods.
  2. You are using something very compute intensive, and do not want to block the UI while the CPU is processing for several seconds.
  3. Wrapping a blocking IO call in a thread to avoid the blocking the UI. (when true Async IO api is not available).

Just changing methods to async and using Task.FromResult will probably not result in any advantage at all since all the code will still run on the main thread, and any cpu intensive operations or blocking calls will still freeze the UI.

If the goal is to avoid UI blocking then Task.Run is probably the way to go. But this will require your program to be threadsafe. You could use a limitedConcurrencyTaskScheduler to move work on a background thread, but still prevent multiple commands from being executed concurrently. This reduces the risk of thread safety problems.

Overall I would recommend re-analyzing what the actual problem is that you are trying to solve. Using asynchronous patterns might be part of such a solution, but it is probably not the only way.

Upvotes: 1

Stephen Cleary
Stephen Cleary

Reputation: 456497

adding asynchronous execution was a good idea to display data about the bench in real time

That's not really what asynchronous execution is for. Asynchrony is all about freeing up threads, which on the client side translates into a responsive UI, and on the server side translates into a more scalable service.

Regarding "display data about the bench in real time", it sounds like what you really want is a form of progress reporting. (Note that "progress" in this context can be any collection of data, not just a percent-complete). The built-in IProgress<T> is made for progress reporting, and works perfectly well with synchronous code even though most of the examples are for asynchronous code.

You would want asynchronous code if your app primarily made I/O-bound requests. But the proper way to make something asynchronous is to start at the lowest levels, e.g., WriteSingleRegister, and then let the asynchrony grow up through the app from there. If you're controlling hardware, asynchrony is possible but likely not the best solution.

Upvotes: 5

Related Questions