I'm learning the usage of async and await, and tried to do the following:
I have an array of numbers in a particular order, and an async method that gets a number and a time delay, and return the same passed-in number.
What I'd like to achieve is to print the numbers in a reveresed order (relative to the calling order), utilizing the time delay.
I'm having a hard time figuring out how to do it, and be glad for a guidance. Here's what I have (which, ofcourse, doesn't work):
public static async Task<int> DelayAndReturn(int number, int milisecDelay)
{
await Task.Delay(milisecDelay);
return number;
}
public static void Main(string[] args)
{
int[] arr = { 1, 2, 3 };
int milisecDelay = 10000;
foreach (int num in arr)
{
Console.WriteLine(DelayAndReturn(num, milisecDelay));
milisecDelay /= 10;
}
Console.ReadLine();
}
DelayAndReturn returns a Task object and the correct way to get the result of that object is to await the task. However, awaiting the task will also stop your foreach until 10000 ms have passed, and only then send the next item for processing.
Note that, although the code execution is waiting for the asynchronous operation to complete, the thread is free to be used by other processes.
Your best bet to get them printed in reversed order, is to create a collection of tasks and await all of them.
public static async Task DelayAndReturn(int number, int milisecDelay)
{
await Task.Delay(milisecDelay);
Console.WriteLine(number);
}
public static void Main(string[] args)
{
PrintReversed().GetAwaiter().GetResult();
}
public static async Task PrintReversed() {
int[] arr = { 1, 2, 3 };
int milisecDelay = 1000;
List<Task> tasks = new List<Task>();
foreach (int num in arr)
{
tasks.Add(DelayAndReturn(num, milisecDelay));
milisecDelay /= 10;
}
await Task.WhenAll(tasks);
}
Related
This question already has answers here:
Captured variable in a loop in C#
(10 answers)
Closed 2 years ago.
Hi I'm trying to make a simple code to run my function in async way. But the result turn out to be quite unexpected. the result i want is like the counter function can run in parallel way and output the result some way similar like:
Start1
End1
Start2
End2
Start3
Start4
End 3
......
Hi
but it turns out it only get the for loop value i=60 into counter function. I'm quite new to async method and google also cant find the appropriate explanation.
namespace Asycn
{
class Program
{
static async Task Main(string[] args)
{
var tasks = new List<Task>();
for (int i = 0; i < 60; i++)
{
tasks.Add(Task.Run(()=>counters(i)));
}
await Task.WhenAll(tasks);
Console.WriteLine("Hi");
}
private static void counters(int num)
{
Console.WriteLine("Start"+num.ToString());
Thread.Sleep(num*1000);
Console.WriteLine("End"+num.ToString());
}
}
}
And below is the running result
Running Result
I assume that you are just getting familiar with async here. Generally when you want to process this number of tasks, it's better to limit parallelism with something like plinq, or Parallel.Foreach
The issue is that i is incremented before the Tasks run.
All you need to do is capture the value within the loop:
namespace Asycn
{
class Program
{
static async Task Main(string[] args)
{
var tasks = new List<Task>();
for (int i = 0; i < 60; i++)
{
var copy = i; // capture state of i
tasks.Add(Task.Run(()=>counters(copy)));
}
await Task.WhenAll(tasks);
Console.WriteLine("Hi");
}
private static void counters(int num)
{
Console.WriteLine("Start"+num.ToString());
Thread.Sleep(num*1000);
Console.WriteLine("End"+num.ToString());
}
}
}
Your code isn't actually using async/await to its fullest potential. You're not capturing the value of i, but you won't have to if you write your code like this:
static async Task Main(string[] args)
{
var tasks = new List<Task>();
for (int i = 0; i < 60; i++)
{
tasks.Add(counters(i));
}
await Task.WhenAll(tasks);
Console.WriteLine("Hi");
}
private static async Task counters(int num)
{
Console.WriteLine("Start"+num.ToString());
await Task.Delay(num*1000);
Console.WriteLine("End"+num.ToString());
}
The output looks like this:
Start0
End0
Start1
Start2
Start3
...
End1
End2
End3
...
Hi
I'm trying to optimize a small program. So here is the basic idea:
I have an array of unfiltered data, and I wanna pass that to a function which will call another function, twice, for data filtering and insertion to a new list. The first call will take the data from original array in range from 0 => half of arrays length, and the second will do the same, but with range from half, to the last item. This way, I should make simultaneous insertion of filtered data into the same list. After the insertion is completed the filtered list can be passed to the rest of the program. Here's the code:
static void Main(string[]
{
// the unfiltered list
int[] oldArray = new int[6] {1,2,3,4,5,6};
// filtered list
List<int> newList= new List<int>();
// Functions is my static class
Functions.Insert(newList, oldArray )
Continue_Program_With_Filtered_List(newList);
// remaining functions...
}
And here is the Function class:
public static class Functions
{
public static void Insert(List<int> newList, int[] oldArray)
{
new Thread(() =>
{
Inserter(newList, oldArray, true);
}).Start();
new Thread(() =>
{
Inserter(newList, oldArray, false);
}).Start();
// I need to wait the result here of both threads
// and make sure that every item from oldArray has been filtered
// before I proceed to the next function in Main()
}
public static void Inserter(List<int> newList, int[] oldArray, bool countUp)
{
bool filterIsValid = false;
int length = oldArray.Length;
int halflen = (int)Math.Floor((decimal)length / 2);
if (countUp)
{
// from half length to 0
for (int i = 0; i < halflen; i++)
{
// filtering conditions here to set value of filterIsValid
if(filterIsValid)
newList.Add(oldArray[i]);
}
}
else
{
// from half length to full length
for (int i = halflen + 1; i < length; i++)
{
// filtering conditions here to set value of filterIsValid
if(filterIsValid)
newList.Add(oldArray[i]);
}
}
}
}
So the problem is that I must await Function.Insert() to complete every thread, and pass through every item before the newList is passed to the next function in Main().
I've no idea how to use Tasks or async method on something like this. This is just an outline of the program by the way. Any help?
In your case using PLINQ may also an option.
static void Main(string[] args)
{
// the unfiltered list
int[] oldArray = new int[6] { 1, 2, 3, 4, 5, 6 };
// filtered list
List<int> newList = oldArray.AsParallel().Where(filter).ToList();
// remaining functions...
}
You can also use AsOrdered() to preserve order
To come back to your initial question, here's what you can do
Note: Solution with minimal changes to your original code, whether there are other possible optimizations or not
Additional Note: Keep in mind that there can still be concurrency issues depending on what else you do with the arguments passing to that function.
public static async Task Insert(List<int> newList, int[] oldArray)
{
ConcurrentBag<int> concurrentBag = new ConcurrentBag<int>();
var task1 = Task.Factory.StartNew(() =>
{
Inserter(concurrentBag, oldArray, true);
});
var task2 = Task.Factory.StartNew(() =>
{
Inserter(concurrentBag, oldArray, false);
});
await Task.WhenAll(task1, task2);
newList.AddRange(concurrentBag);
}
public static void Inserter(ConcurrentBag<int> newList, int[] oldArray, bool countUp)
{
//Same code
}
Edit: Your second for-loop is wrong, change it to this or you will loose one item
for (int i = halflen; i < length; i++)
I have some code that runs thousands of URLs through a third party library. Occasionally the method in the library hangs which takes up a thread. After a while all threads are taken up by processes doing nothing and it grinds to a halt.
I am using a SemaphoreSlim to control adding new threads so I can have an optimal number of tasks running. I need a way to identify tasks that have been running too long and then to kill them but also release a thread from the SemaphoreSlim so a new task can be created.
I am struggling with the approach here so I made some test code that immitates what I am doing. It create tasks that have a 10% chance of hanging so very quickly all threads have hung.
How should I be checking for these and killing them off?
Here is the code:
class Program
{
public static SemaphoreSlim semaphore;
public static List<Task> taskList;
static void Main(string[] args)
{
List<string> urlList = new List<string>();
Console.WriteLine("Generating list");
for (int i = 0; i < 1000; i++)
{
//adding random strings to simulate a large list of URLs to process
urlList.Add(Path.GetRandomFileName());
}
Console.WriteLine("Queueing tasks");
semaphore = new SemaphoreSlim(10, 10);
Task.Run(() => QueueTasks(urlList));
Console.ReadLine();
}
static void QueueTasks(List<string> urlList)
{
taskList = new List<Task>();
foreach (var url in urlList)
{
Console.WriteLine("{0} tasks can enter the semaphore.",
semaphore.CurrentCount);
semaphore.Wait();
taskList.Add(DoTheThing(url));
}
}
static async Task DoTheThing(string url)
{
Random rand = new Random();
// simulate the IO process
await Task.Delay(rand.Next(2000, 10000));
// add a 10% chance that the thread will hang simulating what happens occasionally with http request
int chance = rand.Next(1, 100);
if (chance <= 10)
{
while (true)
{
await Task.Delay(1000000);
}
}
semaphore.Release();
Console.WriteLine(url);
}
}
As people have already pointed out, Aborting threads in general is bad and there is no guaranteed way of doing it in C#. Using a separate process to do the work and then kill it is a slightly better idea than attempting Thread.Abort; but still not the best way to go. Ideally, you want co-operative threads/processes, which use IPC to decide when to bail out themselves. This way the cleanup is done properly.
With all that said, you can use code like below to do what you intend to do. I have written it assuming your task will be done in a thread. With slight changes, you can use the same logic to do your task in a process
The code is by no means bullet-proof and is meant to be illustrative. The concurrent code is not really tested well. Locks are held for longer than needed and some places I am not locking (like the Log function)
class TaskInfo {
public Thread Task;
public DateTime StartTime;
public TaskInfo(ParameterizedThreadStart startInfo, object startArg) {
Task = new Thread(startInfo);
Task.Start(startArg);
StartTime = DateTime.Now;
}
}
class Program {
const int MAX_THREADS = 1;
const int TASK_TIMEOUT = 6; // in seconds
const int CLEANUP_INTERVAL = TASK_TIMEOUT; // in seconds
public static SemaphoreSlim semaphore;
public static List<TaskInfo> TaskList;
public static object TaskListLock = new object();
public static Timer CleanupTimer;
static void Main(string[] args) {
List<string> urlList = new List<string>();
Log("Generating list");
for (int i = 0; i < 2; i++) {
//adding random strings to simulate a large list of URLs to process
urlList.Add(Path.GetRandomFileName());
}
Log("Queueing tasks");
semaphore = new SemaphoreSlim(MAX_THREADS, MAX_THREADS);
Task.Run(() => QueueTasks(urlList));
CleanupTimer = new Timer(CleanupTasks, null, CLEANUP_INTERVAL * 1000, CLEANUP_INTERVAL * 1000);
Console.ReadLine();
}
// TODO: Guard against re-entrancy
static void CleanupTasks(object state) {
Log("CleanupTasks started");
lock (TaskListLock) {
var now = DateTime.Now;
int n = TaskList.Count;
for (int i = n - 1; i >= 0; --i) {
var task = TaskList[i];
Log($"Checking task with ID {task.Task.ManagedThreadId}");
// kill processes running for longer than anticipated
if (task.Task.IsAlive && now.Subtract(task.StartTime).TotalSeconds >= TASK_TIMEOUT) {
Log("Cleaning up hung task");
task.Task.Abort();
}
// remove task if it is not alive
if (!task.Task.IsAlive) {
Log("Removing dead task from list");
TaskList.RemoveAt(i);
continue;
}
}
if (TaskList.Count == 0) {
Log("Disposing cleanup thread");
CleanupTimer.Dispose();
}
}
Log("CleanupTasks done");
}
static void QueueTasks(List<string> urlList) {
TaskList = new List<TaskInfo>();
foreach (var url in urlList) {
Log($"Trying to schedule url = {url}");
semaphore.Wait();
Log("Semaphore acquired");
ParameterizedThreadStart taskRoutine = obj => {
try {
DoTheThing((string)obj);
} finally {
Log("Releasing semaphore");
semaphore.Release();
}
};
var task = new TaskInfo(taskRoutine, url);
lock (TaskListLock)
TaskList.Add(task);
}
Log("All tasks queued");
}
// simulate all processes get hung
static void DoTheThing(string url) {
while (true)
Thread.Sleep(5000);
}
static void Log(string msg) {
Console.WriteLine("{0:HH:mm:ss.fff} Thread {1,2} {2}", DateTime.Now, Thread.CurrentThread.ManagedThreadId.ToString(), msg);
}
}
I have this "simple" test code... (Don't bother the strange use of the Class methods...)
I am trying to grasp the Task<> intricacies... I think I have a little understanding of Task<>.Start()/Task<>.Result pattern (maybe as it resembles more the 'old' Thread.Start()?) but as soon as it seems to me to grasp something (and so I throw in the await keyword)... then all entangles again :-(
Why my code returns immediately after the first task completes? Why it doesn't wait on the Task.WhenAll()?
static BigInteger Factorial(BigInteger factor)
{
BigInteger factorial = 1;
for (BigInteger i = 1; i <= factor; i++)
{
factorial *= i;
}
return factorial;
}
private class ChancesToWin
{
private int _n, _r;
public ChancesToWin(int n, int r)
{
_n = n;
_r = r;
}
private Task<BigInteger> CalculateFactAsync(int value)
{
return Task.Factory.StartNew<BigInteger>(() => Factorial(value));
}
public async Task<BigInteger> getFactN()
{
BigInteger result = await CalculateFactAsync(_n);
return result;
}
public async Task<BigInteger> getFactN_R()
{
BigInteger result = await CalculateFactAsync(_n - _r);
return result;
}
public async Task<BigInteger> getFactR()
{
BigInteger result = await CalculateFactAsync(_r);
return result;
}
}
private async static void TaskBasedChancesToWin_UseClass()
{
int n = 69000;
int r = 600;
List<Task<BigInteger>> tasks = new List<Task<BigInteger>>();
ChancesToWin ctw = new ChancesToWin(n, r);
tasks.Add(ctw.getFactN());
tasks.Add(ctw.getFactN_R());
tasks.Add(ctw.getFactR());
// The getFactR() returns first of the other two tasks... and the code exit!
BigInteger[] results = await Task.WhenAll(tasks);
// I don't get here !!!!
BigInteger chances = results[0] / results[1] * results[2];
//Debug.WriteLine(chances);
}
static void Main(string[] args)
{
TaskBasedChancesToWin_UseClass();
}
Async methods run synchronously until the first await when they return control to the calling method, usually returning a task representing the rest of the asynchronous operation. TaskBasedChancesToWin_UseClass doesn't return a task so the caller can't wait for it to complete. That's why you shouldn't use async void outside of event handlers.
Since Main doesn't wait for the operation your application ends before the operation had a chance to complete.
You would usually wait with await but since you Main can't be an async method you can block synchronously with Wait on the task returned from TaskBasedChancesToWin_UseClass:
async static Task TaskBasedChancesToWin_UseClass()
{
// ...
}
static void Main()
{
TaskBasedChancesToWin_UseClass().Wait();
}
Here's what I'm trying to do:
Get one html page from url which contains multiple links inside
Visit each link
Extract some data from visited link and create object using it
So far All i did is just simple and slow way:
public List<Link> searchLinks(string name)
{
List<Link> foundLinks = new List<Link>();
// getHtmlDocument() just returns HtmlDocument using input url.
HtmlDocument doc = getHtmlDocument(AU_SEARCH_URL + fixSpaces(name));
var link_list = doc.DocumentNode.SelectNodes(#"/html/body/div[#id='parent-container']/div[#id='main-content']/ol[#id='searchresult']/li/h2/a");
foreach (var link in link_list)
{
// TODO Threads
// getObject() creates object using data gathered
foundLinks.Add(getObject(link.InnerText, link.Attributes["href"].Value, getLatestEpisode(link.Attributes["href"].Value)));
}
return foundLinks;
}
To make it faster/efficient I need to implement threads, but I'm not sure how i should approach it, because I can't just randomly start threads, I need to wait for them to finish, thread.Join() kind of solves 'wait for threads to finish' problem, but it becomes not fast anymore i think, because threads will be launched after earlier one is finished.
The simplest way to offload the work to multiple threads would be to use Parallel.ForEach() in place of your current loop. Something like this:
Parallel.ForEach(link_list, link =>
{
foundLinks.Add(getObject(link.InnerText, link.Attributes["href"].Value, getLatestEpisode(link.Attributes["href"].Value)));
});
I'm not sure if there are other threading concerns in your overall code. (Note, for example, that this would no longer guarantee that the data would be added to foundLinks in the same order.) But as long as there's nothing explicitly preventing concurrent work from taking place then this would take advantage of threading over multiple CPU cores to process the work.
Maybe you should use Thread pool :
Example from MSDN :
using System;
using System.Threading;
public class Fibonacci
{
private int _n;
private int _fibOfN;
private ManualResetEvent _doneEvent;
public int N { get { return _n; } }
public int FibOfN { get { return _fibOfN; } }
// Constructor.
public Fibonacci(int n, ManualResetEvent doneEvent)
{
_n = n;
_doneEvent = doneEvent;
}
// Wrapper method for use with thread pool.
public void ThreadPoolCallback(Object threadContext)
{
int threadIndex = (int)threadContext;
Console.WriteLine("thread {0} started...", threadIndex);
_fibOfN = Calculate(_n);
Console.WriteLine("thread {0} result calculated...", threadIndex);
_doneEvent.Set();
}
// Recursive method that calculates the Nth Fibonacci number.
public int Calculate(int n)
{
if (n <= 1)
{
return n;
}
return Calculate(n - 1) + Calculate(n - 2);
}
}
public class ThreadPoolExample
{
static void Main()
{
const int FibonacciCalculations = 10;
// One event is used for each Fibonacci object.
ManualResetEvent[] doneEvents = new ManualResetEvent[FibonacciCalculations];
Fibonacci[] fibArray = new Fibonacci[FibonacciCalculations];
Random r = new Random();
// Configure and start threads using ThreadPool.
Console.WriteLine("launching {0} tasks...", FibonacciCalculations);
for (int i = 0; i < FibonacciCalculations; i++)
{
doneEvents[i] = new ManualResetEvent(false);
Fibonacci f = new Fibonacci(r.Next(20, 40), doneEvents[i]);
fibArray[i] = f;
ThreadPool.QueueUserWorkItem(f.ThreadPoolCallback, i);
}
// Wait for all threads in pool to calculate.
WaitHandle.WaitAll(doneEvents);
Console.WriteLine("All calculations are complete.");
// Display the results.
for (int i= 0; i<FibonacciCalculations; i++)
{
Fibonacci f = fibArray[i];
Console.WriteLine("Fibonacci({0}) = {1}", f.N, f.FibOfN);
}
}
}