Reputation: 59
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
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:
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
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