-2

I've attempted to make a simple step mode for an algorithm I'm running, and here is how it looks like:

    public async Task<bool> AStarAlgorithmAsync(PFSquare curr = null)
    {
        // some algorithm code here
        foreach(var square in Sorroundings)
        {
            if (SteppedMode)
            {
                await Task.Run(Pause);
            }
            if (await AStarAlgorithmAsync(square))
            {
                return true;
            }
        }
    }

In my application, I have a Boolean called SteppedMode that decides if the algorithm should run one iteration per click event.

Pause() looks like this:

    private void Pause()
    {
        while (!ContinueStep) { }
        ContinueStep = false;
        return;
    }

And in another part of my (GUI) application I have an event which sets the boolean ContinueStep to true which in theory should end the while loop and continue the algorithm function. Currently this bit of code locks my GUI thread up and I'm almost certain there is a better way to do this.

I'm trying to get my algorithm function to run one iteration, wait for a click from the user and only then continue running the algorithm. Is there an easier and cleaner way to do this?

(This is a GUI application, not a console application.)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
tempacc555
  • 33
  • 5
  • Instead of putting `SteppedMode` boolean inside your algorithm, how about writing the step mode algorithm as a separate function? then the automated algorithm would simply call this stepped function over and over until the end. – M.kazem Akhgary Jan 25 '20 at 23:36
  • I don't see why the code you posted would have literally locked the GUI thread. It would consume most of the CPU time, which would slow things down a lot. But not lock completely. That said, instead of waiting for a polling method to return, you can just use [`TaskCompletionSource`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcompletionsource-1). See marked duplicate. – Peter Duniho Jan 25 '20 at 23:38
  • @PeterDuniho This question is not really a duplicate. I think OP went down the rabbit hole and trying to fix the wrong problem – M.kazem Akhgary Jan 25 '20 at 23:38
  • @M.kazemAkhgary Good idea, I think I'll try that. – tempacc555 Jan 25 '20 at 23:40
  • @M: it's an almost-identical duplicate. The OP wants their loop to pause with each iteration, but to do so asynchronously. TCS is exactly the way to do that. More generally: they will just create a new TCS with each iteration, as long as the "single-step" mode is active, and await the TCS's task. Even if they refactor so that the body of the loop is a separate method, they still have the issue of waiting for the next step (i.e. the user invokes whatever part of the UI is currently toggling `ContinueStep`) – Peter Duniho Jan 25 '20 at 23:40
  • **Moderator Note** - Please [be nice](http://stackoverflow.com/help/be-nice) in the comment section. – Bhargav Rao Jan 26 '20 at 00:05
  • 1
    @PeterDuniho _"Questions often require closure first, then improvement."_ Not for duplicate closures though (for "needs focus" closures, I wholeheartedly agree). It makes no sense to presume to know the (existing) solution and then be open to the question being (allegedly) altered to a completely different question. This would effectively violate SO community standards as such a change would need to be posted as a wholly separate question. – Flater Jan 26 '20 at 00:05
  • @BhargavRao: I'm not sure how this is not nice. A statement was made that seemingly omits the question content and even explicitly points at the title alone as the reason for judging the question - how is pointing this out being not nice? – Flater Jan 26 '20 at 00:07
  • **Note to editors:** please don't edit the question so as to depart from the essence of the original question; tags; and flagged duplicate just to avoid a close vote and possibly satisfy an answer that was already off-topic –  Jan 26 '20 at 01:36
  • Thank you for helping unmark the question as a duplicate, I'm really worried about my account getting restricted. I understand the criticism about the question itself, I'll do my best to clear it up. I did not get the answer I wanted but that's fine, I'll try implementing a stepped mode instead with some state machine class, I was hoping for a more Task related answer however I'd need to see actual code and I don't know what TaskCompletetionSource actually is, with that being said thank you all for your help! – tempacc555 Jan 26 '20 at 03:50

1 Answers1

2

Your property is moonlighting as a method.

It makes no sense to set a property, to then have that property revert back to its original state immediately. As a consumer, I would be majorly confused by that behavior. Think about this code:

var myObj = new MyObject();

myObj.MyBoolean = true;

Console.WriteLine(myObj.MyBoolean); // FALSE!?

It just doesn't make sense.

The only effect you want to trigger by setting this property is to execute some code. That's exactly what methods are supposed to be used for:

public void ContinueStep()
{
    Console.WriteLine("I did some work");
}

So instead of this:

myObj.ContinueStep = true;

you should be doing this:

myObject.ContinueStep();

This doesn't lock up your UI thread, while also being a lot more sensical to your consumer. The method suggests that some action will be taken (which may or may not lead to state changes in the object - that's a contextual expectation).


Infinite recursion

As an aside; based on your code, AStarAlgorithmAsync is a recursive function, and seemingly infinitely so. There doesn't seem to be an ending condition.
Every recursive level will interate over the first surrounding and then trigger the next level, which again will interate over the first surrounding and then trigger the next level, which again ...

That can't be right, but it's unclear to me how to fix it as the bigger picture is not explained in your question


A simple implementation

What I'm trying to do is get my algorithm function to run one iteration, wait for a click from the user and only then continue running the algorithm, is there an easier and cleaner way to do this?

A simple example of such a thing:

private int _index = 0;

private List<object> _myList = ...; // assume this list contains some elements

public void ProcessNextObject()
{
    if(_index < _myList.Length)
    {
        Process(_myList[_index]);
        _index++;
    }
}

private void Process(object o)
{
    Console.WriteLine("Processing this object!");
}

You can then hook up your click event to call ProcessNextObject().

Note that in this example, the list is processed once and cannot be processed again. By manipulating the index value, you can change that behavior as you like.

Flater
  • 12,908
  • 4
  • 39
  • 62
  • it's not infinitely recursive, there is much more code to that function it's just that it would add complications to reading it. Also, what would go into my ContinueStep function? my method isn't stepped, should I just create a stepped method and call it every time instead? – tempacc555 Jan 25 '20 at 23:40
  • 1
    _"It makes no sense to set a property..."_ - what _"property"_?. _"...to then have that property revert back to its original state"_ - perhaps, but then again that's exactly what the Windows Kernel function `SetEvent` does for _auto-reset events_ –  Jan 25 '20 at 23:45
  • @MickyD: _"What property?"_ `ContinueStep` _"perhaps, but then again that's exactly what the Windows Kernel function SetEvent does"_ Just because someone once did something in a particular way does not in any way imply it's a good approach, nor one that should be replicated in perpetuity. If that were a valid measure of quality, we'd still be using goto statements as well. – Flater Jan 25 '20 at 23:49
  • 1
    Regarding the change around `myObject.ContinueStep();` - the original code is _already_ in a method named `Pause()` so we as readers know the intent without creating another method. Besides, the method is `private` so it's not going to impact callers too much –  Jan 25 '20 at 23:50
  • @MickyD: Public/private is irrelevant for the scope of the question and answer - it doesn't change the issue at hand. Also, purpose of the `Pause` method (not doing anything for now) is quite the opposite of the `ContinueStep` property and method (doing something now), hence why I stuck with the `ContinueStep` name to make it clear what the intention of the code is, i.e. to do something rather than to block everything by not doing something. – Flater Jan 25 '20 at 23:52
  • 1
    Yes perhaps, entirely possible. By the way, the linked duplicate to this question above mentions .NET's `Monitor` and `AutoResetEvent`s (there's that word again). Both exhibit similar behavior. –  Jan 25 '20 at 23:57
  • 1
    How does this address _"Is there a way to `await` a flag change in a function?"_. The fact that your answer is in no way similar to the linked duplicates raises an eyebrow –  Jan 26 '20 at 00:00
  • @MickyD: It does indeed raise an eyebrow, mostly because the linked duplicate in no way answers the posed question - if you read the question and not just the title, that is. I already elaborated on this in the comments under the question in response to the closure so I won't needlessly repeat it here. – Flater Jan 26 '20 at 00:02
  • Haha, take that up with Peter. I'm sure he'll raise an eyebrow ;) –  Jan 26 '20 at 00:03