1

I have a following problem. I have a synchronous .Save() action that takes quite a few minutes to complete. I cannot make it async (3rd party library), so to keep the UI responsive, I am using it all in Task.Run().

I would like to report progress so that the users sees that something is happening - and I want to make it in the Status label - a simple and old school append and remove dots to string, sort of like
Wait.
Wait..
Wait...
Wait.

I don't know how to do it in a simple way - I thought of a while loop that would break once the Task ends, but it doesn't seem to work - either the UI is blocked or the status label does not update.

I tried a couple of things, for example:

bool done = false;
            Task.Run(() =>
            {
                Exporter.DoLongSyncTask();
            }).ContinueWith(r => done = true);
            StatusLabel = ".";
            while (!done)
            {
                if (StatusLabel == "")
                    StatusLabel = ".";
                else if (StatusLabel == ".")
                    StatusLabel = "..";
                else if (StatusLabel == "..")
                    StatusLabel = "";
            }

(StatusLabel is a property with INotifyPropertyChanged implemented, it works OK)

and more complicated

private async Task<bool> DoTheWork()
        {

            await Task.Run(() => TheFile.Save());
            return true;
        }

            private string ReportProgress(string status)
        {
            if (status == ".")
                status = "..";
            else if (status == "..")
                status = "...";
            else if (status == "...")
                status = ".";
            MyProgress.Report(new InDesignExporterProgress { StatusInfo = status });
            return status;
        }

private string ReportProgress(string status)
{
    if (status == ".")
        status = "..";
    else if (status == "..")
        status = "...";
    else if (status == "...")
        status = ".";
    MyProgress.Report(new SomeProgressClass { StatusInfo = status });
    return status;
}       

and the above are called from here

        bool done = false;
        Task.Run(() =>
        {
            done = DoTheWork().Result;
        });
        string status = ".";
        while (!done)
        {
            status = ReportProgress(status);
        }

So, what's the right way?

5
  • 3
    You need to give your UI thread time to do its message pumping. If you can't modify the original method to report progress, one option is to update status periodically based on a timer. Commented Nov 30, 2015 at 23:10
  • done variable that you can are using to signal two threads might not work because some time value is not immediately available to other thread. Commented Nov 30, 2015 at 23:30
  • 1
    @Bartosz Good time to read about async/await Commented Nov 30, 2015 at 23:32
  • @Eser - yes, you're probably right. I read a bit about this and I think I generally understand the concept, can you please point me to an article that could help with this issue? Commented Nov 30, 2015 at 23:36
  • 1
    Don't use that done variable, save the task that was created and use task.IsCompleted Commented Feb 3, 2017 at 20:13

2 Answers 2

4

To know if a task has been completed, check the value of the IsCompleted property.

while (!task.IsCompleted)
{
    // ...
}

But, for what I think you're trying to, I'd do something like this:

using (var timer = new System.Windows.Forms.Timer())
{
    timer.Tick += (s, e) =>
    {
        if (StatusLabel == "")
            StatusLabel = ".";
        else if (StatusLabel == ".")
            StatusLabel = "..";
        else if (StatusLabel == "..")
            StatusLabel = "";
    };
    timer.Interval = 100;
    timer.Enabled = true;

    await Task.Run(() => Exporter.DoLongSyncTask);
}
Sign up to request clarification or add additional context in comments.

Comments

0

I think you are blocking your UI thread with the while loop since I guess it's running on that thread.

I suggest you try to change your while loop like this and the method signature to async Task

public async Task DoSomething(){
        bool done = false;
        Task.Run(() =>
        {
            Exporter.DoLongSyncTask();
        }).ContinueWith(r => done = true);

        StatusLabel = ".";
        while (!done)
        {
            if (StatusLabel == "")
                StatusLabel = ".";
            else if (StatusLabel == ".")
                StatusLabel = "..";
            else if (StatusLabel == "..")
                StatusLabel = "";
            await Task.Delay(200);
        }
}

1 Comment

@Eser, can you please explain? what would be a better approach in this case?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.