Code always waits until currently running tasks have finished before the OperationCancelledException is thrown.
I would like the program to stop immediately on the condition being true.
static void Main()
{
// want to break out of a Parallel.For immediately when a condition occurs
var cts = new CancellationTokenSource();
var po = new ParallelOptions();
po.CancellationToken = cts.Token;
long counterTotal = 0;
try
{
// want to have a sum of counts at the end
Parallel.For<long>(1, 26, po, () => 0, delegate(int i, ParallelLoopState state, long counterSubtotal)
{
po.CancellationToken.ThrowIfCancellationRequested();
Console.WriteLine(i.ToString());
for (int k = 0; k < 1000000000; k++)
{
counterSubtotal++;
if (i == 4 && k == 900000000)
{
cts.Cancel();
// Would like to break out here immediately
}
}
return counterSubtotal;
}, (x) => Interlocked.Add(ref counterTotal, x)
);
}
catch (OperationCanceledException e)
{
Console.WriteLine("Cancelled");
Console.WriteLine("Total iterations across all threads {0}", String.Format("{0:n0}", counterTotal));
Console.ReadLine();
}
}
I found putting a breakpoint on cts.Cancel() and in the catch demonstrates what is happening.
Have looked at state.Stop too.
This is a simplified version of other code.
Perhaps Parallel.For isn't ideal for things which are very long running inside the method if we want to break out immediately.
Update2:
The code now works as expected and gives a good total
static void Main()
{
// want to break out of a Parallel.For immediately when a condition occurs
var cts = new CancellationTokenSource();
var po = new ParallelOptions();
po.CancellationToken = cts.Token;
long counterTotal = 0;
try
{
// want to have a sum of counts at the end
// using type param here to make counterSubtotal a long
Parallel.For<long>(1, 26, po, () => 0, delegate(int i, ParallelLoopState state, long counterSubtotal)
{
Console.WriteLine(i.ToString());
// 1 billion
for (int k = 0; k < 1000000000; k++)
{
//po.CancellationToken.ThrowIfCancellationRequested();
if (po.CancellationToken.IsCancellationRequested)
{
return counterSubtotal;
}
counterSubtotal++;
if (i == 4 && k == 400000000)
{
Console.WriteLine("Inner Cancelled");
cts.Cancel();
}
}
return counterSubtotal;
}, (x) => Interlocked.Add(ref counterTotal, x)
);
}
catch (OperationCanceledException e)
{
Console.WriteLine("Cancelled");
Console.WriteLine("Total iterations across all threads {0}", String.Format("{0:n0}", counterTotal));
Console.ReadLine();
}
}
If you want it to break more "immediately" than you need to check the cancellation token inside of your inner for. As it is now, it will check for cancel before entering, but after that it won't look at the token again.
for (int k = 0; k < 1000000000; k++)
{
po.CancellationToken.ThrowIfCancellationRequested();
counterSubtotal++;
if (i == 4 && k == 900000000)
{
cts.Cancel();
}
}
Related
I am trying to stop the loop when called the function.suppose first i call the function one loop is running and again called that function i need to break the first called loop and start the loop fresh again.
public void HitTimer(int leagueid,int dposition,int teamid,int round)
{
//if (_hubContext == null)
//{
// _hubContext = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
// }
int m = 1;
for (int k = 1; k < 91; k++)
{
System.Threading.Thread.Sleep(1000);
if (k == 90)
{
AutoAddDraftPlayer(leagueid, dposition,teamid,round);
}
else if (_hubContext != null)
{
_hubContext.Clients.All.broadcastTime(90 - k, leagueid, teamid, round);
//hubContext.Clients.Client(Context.ConnectionId).broadcastTime(90 - k);
//Clients.Caller.broadcastTime(90 - k);
}
m++;
}
}
my code is above and let me know how to solve
You could refactor this to be Task based and then use a cancellation token to cancel the first task if the method is called again. The details of how to do all of this is here: https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/task-cancellation
static tokenSource = new CancellationTokenSource();
static CancellationToken ct = tokenSource.Token;
static Task task = null;
public void HitTimer(int leagueid,int dposition,int teamid,int round)
{
if (task != null)
tokenSource.Cancel();
try
{
if (task != null)
task.Wait();
}
catch (AggregateException e)
{
foreach (var v in e.InnerExceptions)
Console.WriteLine(e.Message + " " + v.Message);
}
task = Task.Factory.StartNew(() => DoLoop(leagueid, dposition, teamid, round), tokenSource.Token)
}
public void DoLoop(int leagueid,int dposition,int teamid,int round)
{
int m = 1;
for (int k = 1; k < 91; k++)
{
if (ct.IsCancellationRequested)
{
break;
}
System.Threading.Thread.Sleep(1000);
if (k == 90)
{
AutoAddDraftPlayer(leagueid, dposition,teamid,round);
}
else if (_hubContext != null)
{
_hubContext.Clients.All.broadcastTime(90 - k, leagueid, teamid, round);
//hubContext.Clients.Client(Context.ConnectionId).broadcastTime(90 - k);
//Clients.Caller.broadcastTime(90 - k);
}
m++;
}
}
This question already has answers here:
Captured variable in a loop in C#
(10 answers)
Closed 4 years ago.
First off, I'm new to C#, and this is the first program I've written that uses threads. Sorry if it's a bit basic.
Here's my app:
class Program
{
static void AddNumbers()
{
int count = 0;
for (int counter = 0; counter < 90000000; counter++)
{
count += counter;
}
}
static void Main()
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < 50; i++)
{
// Non-threaded
//AddNumbers();
//Console.WriteLine((i + 1).ToString());
// Threaded
new Thread(() =>
{
//Thread.CurrentThread.IsBackground = true;
AddNumbers();
Thread.Sleep(1000);
Console.WriteLine((i + 1).ToString());
}).Start();
}
stopwatch.Stop();
Console.WriteLine("Time elapsed: {0:hh\\:mm\\:ss}", stopwatch.Elapsed);
Console.ReadLine();
}
}
It simply loops i times and adds a bunch of numbers each time. When I run the AddNumbers() version non-multithreaded, it correctly displays 1, 2, 3... etc, but when I run it multithreaded, it returns the same indexes 2-3 times, skips some, and returns the stopwatch value before all the threads have executed.
Can someone please help spot where my error is and most of all clarify my thinking about how threads work in C#? Thanks!
When I run the AddNumbers() version non-multithreaded, it correctly displays 1, 2, 3... etc, but when I run it multithreaded, it returns the same indexes 2-3 times, skips some
It happens, because i is captured, but changed in outside loop.
Read more about it here:
Captured variable in a loop in C#
http://www.trycatchthat.com/csharp/fundamentals/2016/02/29/csharp-closure-loops.html
In order to make it work properly you need to save a local copy:
for (int i = 0; i < 50; i++)
{
// Non-threaded
//AddNumbers();
//Console.WriteLine((i + 1).ToString());
// Threaded
int iCopy = i;
new Thread(() =>
{
//Thread.CurrentThread.IsBackground = true;
AddNumbers();
Thread.Sleep(1000);
Console.WriteLine((iCopy + 1).ToString());
}).Start();
}
returns the stopwatch value before all the threads have executed
It happens, because you actually never join your threads and wait for them to execute.
It can be achieved by storing all threads and calling thread.Join():
Thread[] threads = new Thread[50];
for (int i = 0; i < 50; i++)
{
// Non-threaded
//AddNumbers();
//Console.WriteLine((i + 1).ToString());
// Threaded
int iCopy = i;
threads[i] = new Thread(() =>
{
//Thread.CurrentThread.IsBackground = true;
AddNumbers();
Thread.Sleep(1000);
Console.WriteLine((iCopy + 1).ToString());
});
thread[i].Start();
}
for (int i = 0; i < 50; i++)
threads[i].Join();
Read more about waiting for threads to complete here:
Create multiple threads and wait all of them to complete
This is how people used to do it before TPL has been invented.
Now, general rule is not to use Thread, but use Task or Parallel instead.
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Task[] tasks = new Task[50];
for (int i = 0; i < 50; i++)
{
int iCopy = i;
tasks[i] = Task.Run(() => {
AddNumbers();
Thread.Sleep(1000);
Console.WriteLine((iCopy + 1).ToString());
});
}
Task.WaitAll(tasks);
Instead of printing o and -, I want to print the number of o and - on the console. However, I have no idea how to get how many times a loop spins in a thread before it gets switched to other threads.
If there are events something likes OnLeaving and OnEntering on a thread, I can get the number of spins in the given time slice. Unfortunately, I have no such events.
class Program
{
public const int N = 1000;
static void Main(string[] args)
{
ThreadStart ts = DoWork;
Thread t = new Thread(ts);
t.Start();
for (int x = 0; x < N; x++)
{
Console.Write('o');
}
t.Join();
}
private static void DoWork()
{
for (int x = 0; x < N; x++)
{
Console.Write('-');
}
}
}
Could you show me how to do this scenario?
To reiterate: AbhayDixit's comment gives you a cycle counter. You just need to add a locking mechanism and reset the counters after a context switch.
I have modified your code to include a cycle counter. Note that I have increased N significantly. Otherwise, one thread will just run through its 1000 iterations at once - because you no longer have a Write() instruction to slow it down.
class Program
{
public const int N = 1000000;
private static object _l = new object();
private static int _i = 0;
private static int _j = 0;
static void Main(string[] args)
{
ThreadStart ts = DoWork;
Thread t = new Thread(ts);
t.Start();
for (int x = 0; x < N; x++)
{
lock (_l)
{
// j-thread has run?
if (_j > 0)
{
// print and reset j
Console.Write("j{0} ", _j);
_j = 0;
}
_i++;
}
}
t.Join();
// print remaining cycles
// one of the threads will have run after the other has finished and
// hence not been printed and reset.
if (_i > 0)
Console.Write("i{0} ", _i);
if (_j > 0)
Console.Write("j{0} ", _j);
Console.ReadKey();
}
private static void DoWork()
{
for (int x = 0; x < N; x++)
{
lock (_l)
{
// i-thread has run?
if (_i > 0)
{
// print and reset i
Console.Write("i{0} ", _i);
_i = 0;
}
_j++;
}
}
}
}
The following code does not work as I expect it. What am I doing wrong? Output is different on every run. Is there a better way of doing this? Assume action does something more complex than what's below.
Action<int> action = (int m) =>
{
if ((m % 2) == 0)
Console.WriteLine("Even");
else
Console.WriteLine("Odd");
};
const int n = 10;
Task[] tasks = new Task[n];
for (int i = 0; i < n; i++)
{
tasks[i] = Task.Factory.StartNew(() => action(i+1));
}
Task.WaitAll(tasks);
The lambda in your loop is capturing a reference to the same i variable every time through the loop, not its value.
Change your loop to something like:
for (int i = 0; i < n; i++)
{
var j = i;
tasks[i] = Task.Factory.StartNew(() => action(j+1));
}
Note that the output will still be different on every run, but you should get exactly five even and five odd outputs.
Let's say I queue those two methods in a for loop
for (int i = 0; i < 100; i++)
{
ThreadPool.QueueUserWorkItem(s =>
{
Console.WriteLine("Output");
Thread.Sleep(1000);
});
}
for (int i = 0; i < 100; i++)
{
ThreadPool.QueueUserWorkItem(s =>
{
Console.WriteLine("Output2");
Thread.Sleep(1000);
});
}
Is there a way to stop all the threads that output Console.WriteLine("Output2"); but keep the ones running that output Console.WriteLine("Output"); ?
You could use a CancellationToken:
for (int i = 0; i < 100; i++)
{
ThreadPool.QueueUserWorkItem(s =>
{
Console.WriteLine("Output");
Thread.Sleep(1000);
});
}
CancellationTokenSource cts = new CancellationTokenSource();
for (int i = 0; i < 100; i++)
{
ThreadPool.QueueUserWorkItem(s =>
{
CancellationToken token = (CancellationToken) s;
if (token.IsCancellationRequested)
return;
Console.WriteLine("Output2");
token.WaitHandle.WaitOne(1000);
}, cts.Token);
}
cts.Cancel();
No, you can't do that. If you want to do something a long the lines then you must write some code to manage it. At the very basic level you need something like this:
object syncObject = new object();
bool shouldOutput2 = true;
ThreadPool.QueueUserWorkItem(s =>
{
lock(syncObject)
{
if(!shouldOutput2)
{
return;
}
}
Console.WriteLine("Output2");
Thread.Sleep(1000);
});
Once you queue the items, then you can set the flag in order to tell the remaining items not to execute:
lock(syncObject)
{
shouldOutput2 = false;
}
This is a very dirty way of doing it, but it seems like the only way given your example. If you can tell us more about what is the actual real-world behavior you're trying to accomplish, then there could be some better options.