Task - Thread

Markus Freitag 3,791 Reputation points
2023-06-14T17:37:53.3966667+00:00

What is better. Maybe async, await is not so complicated.

Can I say it like this?

We do the functions synchronously, who needs it asynchronously must solve this himself.

The used source https://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-dont-use.html

Conclusion: do not use Task.Run in the implementation of the method; instead, use Task.Run to call the method.

The key here is that the solution does not belong in the service. It belongs in the UI layer itself.

Let the UI layer solve its own problems and leave the service out of it.

var result = await Task.Run(() => MyServerData.IsProductResult(77));

return result;

I have a very difficult time with the spelling. How can I still write this? How do I pass parameters? I have seen something like this.

var result = await Task.Run((t1,t2,t3) => XXX t3 is the return value. t1,t2 the passing value?

How do I **cancel **the function, the function when I would click cancel. Is there a pattern way? Here my sample code.

public class ServerData
{
	public int Index { set; get; }
	public string Code { set; get; }
	public string BestBefore { set; get; }
}

public class ServerDataList : List<ServerData>
{
	public bool DataReady { set; get; }

	public void GetServerData()
	{
		DataReady = false;
		for (int i = 0; i < 300000; i++)
		{
			Add(new ServerData()
			{
				BestBefore = "12/2023",
				Code = $"{i,22:D12}",
				Index = i
			});
			Trace.WriteLine($"Code= '{this[Count - 1].Code}',   ThreadID= '{Thread.CurrentThread.ManagedThreadId}'");
			Thread.Sleep(2);
		}
		DataReady = true;
	}

	public bool IsDataFromServerReady()
	{
		while (!DataReady)
		{
			Thread.Sleep(50);
		}
		return DataReady;
	}

	public bool IsProductResult(int amount)
	{
		if (amount > 30)
			return true;
		return false;
	}
}

public class MyService
{
	ServerDataList MyServerData = new ServerDataList();


	public int Process()
	{
		// Tons of work to do in here!
		for (int i = 0; i != 10000000; ++i)
		{
			Trace.WriteLine($"Work {i},   ThreadID= '{Thread.CurrentThread.ManagedThreadId}'");

			if (i == 10000)
				EventIn();


			if (i == 10100)
			{
				EventStart();
				// break;
			}

			if (i == 10900)
			{
				var ret = EventOut();
				// break;
			}
		}
		return 42;
	}

	public async void EventIn()
	{
		await Task.Run(() => MyServerData.GetServerData());

		MessageBox.Show("EventIn");
	}

	public async void EventStart()
	{
		//ServerDataList MyServerDataSub = new ServerDataList();
		await Task.Run(() => MyServerData.IsDataFromServerReady());

		MessageBox.Show("EventStart");
	}

	public async Task<bool> EventOut()
	{
		var result = await Task.Run(() => MyServerData.IsProductResult(77));
		return result;

		// return await Task.Run(() => MyServerData.IsProductResult(45)).Result;

	}
}

MyService MyServiceObject = new MyService();

private async void btnTASK_Click(object sender, EventArgs e)
{
	MyServiceObject = new MyService();
	await Task.Run(() => MyServiceObject.Process());

	MessageBox.Show("TEST Mandelbrot Ready");
}


Developer technologies | C#
Developer technologies | C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
{count} votes

Answer accepted by question author
  1. Bruce (SqlWork.com) 82,061 Reputation points Volunteer Moderator
    2023-06-14T20:20:57.9066667+00:00

    the article you link, states that you should not make a sync method async, the caller should do this.

    but you appear to be faking async for a prototype. A better prototype would supply the task. one would expect the GetServerData to do a async network or database call in real life.

    and from your sample code its not clear why you need to use a Task.

    public Task<List<ServerData>> GetServerDataAsync()
    {
        return Task.Run(() =>
        {
            var list = new List<ServerData>()
    		for (int i = 0; i < 300000; i++)
    		{
    			list.Add(new ServerData()
    			{
    				BestBefore = "12/2023",
    				Code = $"{i,22:D12}",
    				Index = i
    			});
    			Trace.WriteLine($"Code= '{this[Count - 1].Code}',   ThreadID= '{Thread.CurrentThread.ManagedThreadId}'");
    			Thread.Sleep(2);
    		}
            return list;
        });
    }
    
    

    then call:

    // threads should only read this data.
    // if update required use locking
    var serverData = await GetServerDataAsync();
    

    also your process loop is suspect. the event calls are Tasks, but you don't await. the for loop will be fast, so EventStart() be be called before EventIn() has finished. Other than adding a few milliseconds the for loop performs no useful function. the code could be written as:

    public int Process()
    {
    	EventIn();
        EventStart();
        EventOut();
        
        // will return before above tasks complete
        return 42;
    }
    

2 additional answers

Sort by: Most helpful
  1. P a u l 10,766 Reputation points
    2023-06-14T18:42:25.5133333+00:00

    I may be misunderstanding your questions but:

    1. How do you pass inputs to Task.Run? You can't, but you can capture variables from the scope that you run it from. For example:
    var a = 1;
    var b = 2;
    Task.Run(() => Console.WriteLine($"A: {a}, B: {b}"));
    

    In the snippet above the function passed to Task.Run can access a and b. If you pass variables to your EventIn, EventStart and EventOut methods then the function you pass to Task.Run will also be able to access them.

    1. How do you cancel a long running CPU-bound task, such as a for loop counting to 10000000?

    You could create a TaskCancellationSource, which you can use to cancel your loop by checking whether a cancellation has been requested on every iteration. For example:

    var cts = new CancellationTokenSource();
    
    // Simulate something running on another thread cancelling the work.
    
    Task.Run(async () => {
    	await Task.Delay(50); // Delay for 50ms
    
    	cts.Cancel(); // Cancel the long-running task
    });
    
    Task.Run(() => MethodThatDoesLongRunningTask(cts.Token));
    
    void MethodThatDoesLongRunningTask(CancellationToken cancel) {
    	int i;
    
    	for (i = 0; i < 1000000000 && !cancel.IsCancellationRequested; i++) {
    		// Do some work here
    	}
    
    	Console.WriteLine($"This method got up to {i} before it was cancelled!");
    }
    

    The snippet above creates a TaskCancellationSource, schedules a cancellation in 50ms on another Task, schedules the CPU-bound work (a for loop, as per your example), then once the .Cancel() method is called the !cancel.IsCancellationRequested condition will fail and terminate the loop, which in turn will allow the containing Task to also terminate.


  2. SurferOnWww 4,951 Reputation points
    2023-06-15T00:49:36.6733333+00:00

    Conclusion: do not use Task.Run in the implementation of the method; instead, use Task.Run to call the method.

    I suggest that you do not use Task.Run in ASP.NET application in any way. Please see the following Microsoft document:

    Avoid blocking calls

    "ASP.NET Core already runs app code on normal Thread Pool threads, so calling Task.Run only results in extra unnecessary Thread Pool scheduling."

    0 comments No comments

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.