Petr Klíč
Petr Klíč

Reputation: 11

Simple wait till background process ends C#

I have this (probably trivial problem). Im moving cards on form and in one process I need to wait, till card finishes moving (and is added into CardDeck class). For class design reasons I cant add card to new Deck imidietely. All I need is to wait with continuing in code for several ticks on timer (which is running on background). I found waiting routine here on Stackoverflow, but it dosnt work in this case and Im lost in using Process. Is there any simple way to wait some miliseconds, while programm is still running (timer ticking, cards moving on each tick). Im not sure if this can be done without threading, but I hope it is.

private void buCompW1W2_Click(object sender, EventArgs e) {
        //starts moving card to new position
        DeckPOne.MoveCardFromTopToTop(WarPOne);
        DeckPTwo.MoveCardFromTopToTop(WarPTwo);
        //code I hope to enter to delay next steps
        Stopwatch stopwatch = Stopwatch.StartNew();
        while (true) { //AwaitingReceiving indicates is Deck expects to receive card, 0 means, card is already there
            if (WarPOne.AwaitingRecieving == 0 && WarPTwo.AwaitingRecieving == 0)
                break;
        }
        //this test must be done after cards arrive to new place, else its error (-1)
        //test compares cards in WarPOne and WarPTwo, but cards are not there yet (cards are waiting for timer tick to change position etc.) so it returns
        //en error, mechanicaly done it works like intended
        switch (CompareCards(WarPOne.TopCard, WarPTwo.TopCard)) {
            case -1: tbRecord.Text += "\n error comparing"; break;
            case 1: tbRecord.Text += "\n P1 won."; break;
            case 2: tbRecord.Text += "\n P2 won."; break;
            case 0: {
                    tbRecord.Text += "\n WAR!";
                    for (int i = 0; i < 3; i++) {
                        DeckPOne.MoveCardFromTopToTop(WarPOne);
                        DeckPTwo.MoveCardFromTopToTop(WarPTwo);
                    }
                }; break;
        }
    }

Upvotes: 0

Views: 363

Answers (3)

user3163684
user3163684

Reputation: 104

You already accepted another answer, but DoEvents really isn't how this would be handled nowadays. Using "tasks" it would be really easy to move the work to a managed background thread and keep the UI responsive:

private void buCompW1W2_Click(object sender, EventArgs e)
{
            Task t1 = Task.Run(() =>
            {
                //starts moving card to new position
                DeckPOne.MoveCardFromTopToTop(WarPOne);
                DeckPTwo.MoveCardFromTopToTop(WarPTwo);
                //code I hope to enter to delay next steps
                Stopwatch stopwatch = Stopwatch.StartNew();
                while (true)
                { //AwaitingReceiving indicates is Deck expects to receive card, 0 means, card is already there
                  if (WarPOne.AwaitingRecieving == 0 && WarPTwo.AwaitingRecieving == 0)
                    break;
                }
                //this test must be done after cards arrive to new place, else its error (-1)
                //test compares cards in WarPOne and WarPTwo, but cards are not there yet (cards are waiting for timer tick to change position etc.) so it returns
                //en error, mechanicaly done it works like intended
                switch (CompareCards(WarPOne.TopCard, WarPTwo.TopCard))
                {
                    case -1: tbRecord.Text += "\n error comparing"; break;
                    case 1: tbRecord.Text += "\n P1 won."; break;
                    case 2: tbRecord.Text += "\n P2 won."; break;
                    case 0:
                        {
                            tbRecord.Text += "\n WAR!";
                            for (int i = 0; i < 3; i++)
                            {
                                DeckPOne.MoveCardFromTopToTop(WarPOne);
                                DeckPTwo.MoveCardFromTopToTop(WarPTwo);
                            }
                        }; break;
                }
            });
}

This would actually start the work and then return control to the user. So while this is running, the user could click the button again. If this not what you want, you should disable the button until the task is finished. This could be done by adding

buCompW1W2.IsEnabled = false;

at the beginning of the method and

Dispatcher.Invoke(() => buCompW1W2.IsEnabled = true);

at the end of the task. You need to use the dispatcher on the second call, because UI elements can only be accessed from the UI thread.

Upvotes: 0

Dharani Kumar
Dharani Kumar

Reputation: 457

When you do this

while (true) {

        }

on your main thread, There are lot of chances that your timer will be stuck because CLR gives preference to the Main/UI thread than to a timer or a background thread.

All the signals like timer tick,etc will be on MessageQueue and will not be processed until your Main thread is done .

You have two choices

Put

 While(true)
    {
    if (WarPOne.AwaitingRecieving == 0 && WarPTwo.AwaitingRecieving == 0)
                break;

    Application.DoEvents(); // worst thing to do, but it works, flushes the message queue
    } 

else go for threading, Latest feature in threading called Task is robust and can make things easier for you

Upvotes: 1

Thomas D.
Thomas D.

Reputation: 1051

If you really want to just wait for some timer ticks, why don't you subscribe to the timer Elapsed event and wait until "several" ticks have passed? (assuming you are talking about standard timers)

However, notifying the waiting component when the card finally arrives in the deck would possibly be a better option. You could use something like the AutoResetEvent provided by the framework or fire an CardReceived event in the CardDeck class when a new card arrives, which the waiting component could subscribe to.

Upvotes: 0

Related Questions