-4

I want to do some work with a coded in Delay with the UI active.

I thought I had TaskOfTResult_MethodSync fixed with Task.Delay(delay).Wait();. The problem is that blocks Task<int> taskS = TaskOfTResult_MethodSync(10000); it does not immediately move on to the await and the UI is not responsive. UI does not even paint until after the delay.

public async void TestTask3(int delay, CancellationToken ct) works but it does not let me return a value.

If I use

int rslt = TestTask3(1000, token).Result;
public async Task<int> TestTask3(int delay, CancellationToken ct)

It hangs on int rslt = TestTask3(1000, token).Result;

This is not valid syntax await TestTask3(1000, token);

I don't mind using public async void TestTask3(int delay, CancellationToken ct) but I would rather have work done with programmed Delay or Sleep that returns a value (rather than update a public property from the async method). What I want is to kick off a method / task then do some other work from the main tread. Then await. After the await some buttons get activated.

The reason I need a programmed in delay is it reads some instrument data and it needs to wait between reads to get a sample set.

Is there a way to get what I want?

I currently get what I need with BackGroundWorker but that but I was hoping to get there with task.

public MainWindow()
{
    this.DataContext = this;
    InitializeComponent();

    Debug.WriteLine("before Task<int> taskS = TaskOfTResult_MethodSync(10000);");
    Task<int> taskS = TaskOfTResult_MethodSync(10000);
    Debug.WriteLine("before taskS.Wait");
    taskS.Wait();
    Debug.WriteLine("after  taskS.Wait");
    int i = taskS.Result;
    Debug.WriteLine($"i = {i}");
    Debug.WriteLine($"");

    CancellationTokenSource source = new CancellationTokenSource();
    CancellationToken token = source.Token;
    Debug.WriteLine("before TestTask3(1000, token);");
    TestTask3(1000, token);
    Debug.WriteLine("after TestTask3(1000, token);");
    Thread.Sleep(2000);
    Debug.WriteLine("after sleep on main TestTask3(1000, token);");
    source.Cancel();

    Debug.WriteLine("done Main");
}

public async void TestTask3(int delay, CancellationToken ct)
{
    Debug.WriteLine($"TestTask3");
    int ans = -1;
    ans = await Task.Run(async () =>
    {
        // … do compute-bound work here 
        for (int j = 101; j <= 120; j++)
        {
            if(ct.IsCancellationRequested)
            {
                break;
            }
            await Task.Delay(delay);
            ans = j;
            Answer = j;
        }
    });
    Debug.WriteLine($"TestTask3   {ans}");
    Answer = ans;
    //return ans;
}        
Task<int> TaskOfTResult_MethodSync(int delay)
{
    Debug.WriteLine($"TaskOfTResult_MethodSync delay = {delay}  {DateTime.Now}");
    int hours = 10; 
    Task.Delay(delay).Wait();
    Debug.WriteLine($"TaskOfTResult_MethodSync after delay   {DateTime.Now}");
    return Task.FromResult(hours);
}
paparazzo
  • 42,665
  • 20
  • 93
  • 158
  • Both .Wait() and .Result block. – mm8 May 11 '18 at 15:23
  • @mm8 Yes. .Result not only blocks it hangs - does not move on and no error. – paparazzo May 11 '18 at 15:25
  • I ran your code and it doesn't hang for me. (I did have to add an `Answer` property and remove the `ans =` from in front of the `await Task.Run`). It does block for the expected 12 seconds (10 + 2). –  May 11 '18 at 15:43

2 Answers2

2

It's a bit unclear what you are trying to do here but both Wait() and Result will block synchronously.

You should make the TaskOfTResult_MethodSync method async and await Task.Delay:

async Task<int> TaskOfTResult_MethodSync(int delay)
{
    Debug.WriteLine($"TaskOfTResult_MethodSync delay = {delay}  {DateTime.Now}");
    int hours = 10;
    await Task.Delay(delay);
    Debug.WriteLine($"TaskOfTResult_MethodSync after delay   {DateTime.Now}");
    return hours;
}

This makes the method asynchronous method.

Since the constructor is not asynchronous you could then move the invocation of the asynchronous method to a Loaded event handler. And TestTask3 should not return void if you want to be able to await it. Change the return type to Task and await this one also.

Finally, if call Thread.Sleep on the UI thread, your UI will indeed freeze. Use await Task.Delay if you want to "sleep" asynchronously:

public MainWindow()
{
    this.DataContext = this;
    InitializeComponent();

    Loaded += async (s, e) =>
    {
        Debug.WriteLine("before Task<int> taskS = TaskOfTResult_MethodSync(10000);");
        int i = await TaskOfTResult_MethodSync(10000);
        Debug.WriteLine($"i = {i}");
        Debug.WriteLine($"");

        CancellationTokenSource source = new CancellationTokenSource();
        CancellationToken token = source.Token;
        Debug.WriteLine("before TestTask3(1000, token);");
        await TestTask3(1000, token);
        Debug.WriteLine("after TestTask3(1000, token);");
        await Task.Delay(2000);
        Debug.WriteLine("after sleep on main TestTask3(1000, token);");
        source.Cancel();

        Debug.WriteLine("done Main");
    };
}

public async Task TestTask3(int delay, CancellationToken ct)
{
    Debug.WriteLine($"TestTask3");
    await Task.Run(async () =>
    {
        // … do compute-bound work here 
        for (int j = 101; j <= 120; j++)
        {
            if (ct.IsCancellationRequested)
            {
                break;
            }
            await Task.Delay(delay);
            ans = j;
            Answer = j;
        }
    });
    Debug.WriteLine($"TestTask3   {ans}");
    Answer = ans;
}
mm8
  • 135,298
  • 10
  • 37
  • 59
  • That sleep on the main was just to test the source.Cancel();. I should have taken it out. – paparazzo May 11 '18 at 15:37
  • Well, what about the rest? – mm8 May 11 '18 at 15:37
  • I am getting a syntax error on `ans = await Task.Run(async () =>` or cannot convert type void to int. – paparazzo May 11 '18 at 15:50
  • 1
    @paparazzo the lambda expression doesn't return a value so there isn't anything to be assigned to `ans`. You are assigning the value in the lambda so you don't need to assign a result. You can just `await` the `Task.Run`. –  May 11 '18 at 15:54
  • @AdamKingsley Yes I just figured that out. Removing ans = fixed the syntax error. – paparazzo May 11 '18 at 15:57
  • It is running but there is a UI delay somewhere. – paparazzo May 11 '18 at 15:58
  • @paparazzo the delays I had were 10 seconds from the `TaskS` then 2 seconds from `Thread.Sleep(2000)` then 1 second from the `delay` in `TestTask3` –  May 11 '18 at 16:00
  • 1
    Yes, the and variable is superfluous. I removed it. You are supposed to get asynchronous delays. – mm8 May 11 '18 at 19:45
1

One of the first issues that I noted in your code is that you are making all this async calls in your constructor (that's why you can't await TestTask3). I suggest you refer to this question to understand why this is bad.

Now, if you move your code to not be in your constructor, then you don't need to use the blocking constructs (like Wait() and .Result) and use await instead. For example, here is what TaskOfTResult_MethofSync implementation could look like:

private async Task<int> TaskOfTResult_MethodSync(int delay)
{
    await Task.Delay(delay);
    return Task.FromResult(10);
}

Note how instead of blocking on the .Delay call it now awaits

Pedro
  • 2,140
  • 1
  • 17
  • 22
  • Yes that worked. What I cannot figure out is how to get in information back. `int i = await TaskOfTResult_MethodSync(10000);` just stalls (never finishes); – paparazzo May 11 '18 at 20:54