According to this Stack Overflow discussion,using Thread.Sleep() is almost always a bad idea. How would I refactor my code to use a timer instead. I tried to make a start by doing the following:
namespace Engine
{
internal class Program
{
public static DbConnect DbObject = new DbConnect();
System.Timers.Timer timer = new System.Timers.Timer();
// error here
timer.Interval = 2000;
timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
timer.Enabled=false;
}
}
but kept getting a cannot resolve symbol error message.
namespace Engine
{
internal class Program
{
public static DbConnect DbObject = new DbConnect();
private static void Main()
{
SettingsComponent.LoadSettings();
while (true)
{
try
{
for (int x = 0; x < 4; x++)
{
GenerateRandomBooking();
}
Thread.Sleep(2000);
GenerateRandomBids();
AllocateBids();
Thread.Sleep(2000);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
}
}
If you are using .Net 4.5 or later, you can use await instead of a Timer.
For example:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Demo
{
public static class Program
{
private static void Main()
{
Console.WriteLine("Generating bids for 30 seconds...");
using (var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30)))
{
var task = GenerateBids(cancellationTokenSource.Token);
// You can do other work here as required.
task.Wait();
}
Console.WriteLine("\nTask finished.");
}
private static async Task GenerateBids(CancellationToken cancel)
{
while (!cancel.IsCancellationRequested)
{
Console.WriteLine("");
try
{
for (int x = 0; x < 4; x++)
GenerateRandomBooking();
await Task.Delay(2000);
if (cancel.IsCancellationRequested)
return;
GenerateRandomBids();
AllocateBids();
await Task.Delay(2000);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
private static void AllocateBids()
{
Console.WriteLine("AllocateBids()");
}
private static void GenerateRandomBids()
{
Console.WriteLine("GenerateRandomBids()");
}
private static void GenerateRandomBooking()
{
Console.WriteLine("GenerateRandomBooking()");
}
}
}
you can convert your code this one without using Thread.Sleep()
private static void Main()
{
SettingsComponent.LoadSettings();
//while (true)
{
try
{
RaiseRandomBooking(null);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
static void RaiseRandomBooking(object state)
{
for (int x = 0; x < 4; x++)
{
GenerateRandomBooking();
}
System.Threading.Timer tmr = new System.Threading.Timer(RaiseRandomBids, null, 0, 2000);
}
static void RaiseRandomBids(object state)
{
GenerateRandomBids();
AllocateBids();
System.Threading.Timer tmr = new System.Threading.Timer(RaiseRandomBooking, null, 0, 2000);
}
Related
How to properly close console, Without causing the loss of the data received.
the Task Count is increased when the event is received, and the Task Count is decreased when the event is over. When OnStopping, while is executed until the Task count is 0, it ends.
public class TaskHandler
{
private static int TaskCount = 0;
private static object obj = new object();
public static void TaskStart()
{
lock (obj)
{
TaskCount++;
}
}
public static void TaskEnd()
{
lock (obj)
{
TaskCount--;
}
}
public static int GetTaskCount => TaskCount;
}
private void OnStopping()
{
timer.Elapsed -= timerHandler;
// Is there any way to ensure that Event execution is complete?
while (TaskHandler.GetTaskCount != 0)
{
_logger.LogInformation($"Task Count:{TaskHandler.GetTaskCount}...");
Thread.Sleep(3000);
}
_logger.LogInformation($"Task Count: 0... Can close...");
_logger.LogInformation("close...");
}
private static void timerStart()
{
timer.Interval = 1000;
timer.Elapsed += timerHandler;
timer.Start();
}
private static void timerHandler(object sender, EventArgs e)
{
try
{
TaskHandler.TaskStart();
Console.WriteLine("Delay Start");
Thread.Sleep(10000);
Console.WriteLine("Delay End");
TaskHandler.TaskEnd();
}
catch (Exception ex)
{
}
}
I'm messing around with multithreading and making some sort of task engine. The idea is that the engine can have a configurable amount of threads waiting and when a new task arrives the first free thread picks it up and executes it.
The problem is that something 2 threads pickup the same task somehow. I looked it through and I think that this code should work but obviously it doesn't. If I add the 10ms sleep where it is now commented out it works, but I'm not sure I understand why. It looks like the .Reset() function returns before it actually resets the event?
Can somebody explain? Is there a better way to let only a single thread continue when there are multiple waiting?
Thanks
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace TaskTest
{
public class Engine
{
private ManualResetEvent taskEvent;
private ConcurrentQueue<Task> tasks;
private bool running;
private List<Thread> threads;
private int threadAmount;
private int threadsBusy = 0;
public Engine(int amountOfThreads)
{
taskEvent = new ManualResetEvent(false);
tasks = new ConcurrentQueue<Task>();
threads = new List<Thread>();
threadAmount = amountOfThreads;
}
public void Start()
{
running = true;
for (var i = 0; i < threadAmount; i++)
{
var thread = new Thread(Process);
thread.Name = "Thread " + i;
threads.Add(thread);
thread.Start();
}
}
public void Stop()
{
running = false;
taskEvent.Set();
threads.ForEach(t => t.Join());
}
private void Process()
{
while (running)
{
lock (taskEvent)
{
// Lock it so only a single thread is waiting on the event at the same time
taskEvent.WaitOne();
taskEvent.Reset();
//Thread.Sleep(10);
}
if (!running)
{
taskEvent.Set();
return;
}
threadsBusy += 1;
if (threadsBusy > 1)
Console.WriteLine("Failed");
Task task;
if (tasks.TryDequeue(out task))
task.Execute();
threadsBusy -= 1;
}
}
public void Enqueue(Task t)
{
tasks.Enqueue(t);
taskEvent.Set();
}
}
}
EDIT
Rest of the code:
namespace TaskTest
{
public class Start
{
public static void Main(params string[] args)
{
var engine = new Engine(4);
engine.Start();
while (true)
{
Console.Read();
engine.Enqueue(new Task());
}
}
}
}
namespace TaskTest
{
public class Task
{
public void Execute()
{
Console.WriteLine(Thread.CurrentThread.Name);
}
}
}
When using Console.Read() on a key press, two characters are read from the input. You should use Console.ReadLine() instead.
Note that your code can be simplified a lot by using a BlockingCollection to handle the synchronization:
public class Engine
{
private BlockingCollection<Task> tasks;
private List<Thread> threads;
private int threadAmount;
public Engine(int amountOfThreads)
{
tasks = new BlockingCollection<Task>();
threads = new List<Thread>();
threadAmount = amountOfThreads;
}
public void Start()
{
for (var i = 0; i < threadAmount; i++)
{
var thread = new Thread(Process);
thread.Name = "Thread " + i;
threads.Add(thread);
thread.Start();
}
}
public void Stop()
{
tasks.CompleteAdding();
threads.ForEach(t => t.Join());
}
private void Process()
{
foreach (var task in tasks.GetConsumingEnumerable())
{
task.Execute();
}
}
public void Enqueue(Task t)
{
tasks.Add(t);
}
}
I have written a simple Producer-Consumer queue. It keeps throwing an error that the safe handle is disposed.
namespace ConsoleApplication1
{
public class ProducerConsumer:IDisposable
{
private Queue<string> tasks = new Queue<string>();
Thread work;
readonly object obj=new object();
EventWaitHandle wh = new AutoResetEvent(true);
public ProducerConsumer()
{
work = new Thread(doWork);
work.Start();
}
public void doWork()
{
while (true)
{
string task = null;
wh.WaitOne();
lock (obj){
if (tasks.Count > 0)
{
task = tasks.Dequeue();
if (task == null) return;
else
{
Console.WriteLine("Performing task: " + task);
Thread.Sleep(1000); // simulate work...
}
}
}
}
}
public void ShutDown()
{
tasks.Enqueue(null);
wh.Close();
work.Join();
}
public void Dispose()
{
tasks.Enqueue(null);
wh.Close();
work.Join();
}
public void AddTask(string str)
{
lock(obj){tasks.Enqueue(str);wh.Set();}
}
}
class Program
{
static EventWaitHandle _waitHandle = new AutoResetEvent(false);
static void Main(string[] args)
{
ProducerConsumer prod = new ProducerConsumer();
prod.AddTask("Started");
for (int i = 0; i < 5; i++) prod.AddTask(Convert.ToString(i));
prod.AddTask("Done");
prod.ShutDown();
Console.ReadLine();
}
}
}
If I change the wh.WaitOne() to the else part then it starts working
public void doWork()
{
while (true)
{
string task = null;
lock (obj){
if (tasks.Count > 0)
{
task = tasks.Dequeue();
if (task == null) return;
else
{
Console.WriteLine("Performing task: " + task);
Thread.Sleep(1000); // simulate work...
}
}
else
wh.WaitOne();
}
}
}
Can somebody throw a light on why this is happening?
I'm facing a weird bug.
I have something like 100 long running tasks and I want to run 10 of them in the same time.
I found something very similar to my need here : http://msdn.microsoft.com/en-us/library/hh873173%28v=vs.110%29.aspx in the Throttling section.
Here the C# code after simplification :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
public class Program
{
static void Main(string[] args)
{
Test();
}
public static async void Test()
{
var range = Enumerable.Range(1, 100).ToList();
const int CONCURRENCY_LEVEL = 10;
int nextIndex = 0;
var matrixTasks = new List<Task>();
while (nextIndex < CONCURRENCY_LEVEL && nextIndex < range.Count())
{
int index = nextIndex;
matrixTasks.Add(Task.Factory.StartNew(() => ComputePieceOfMatrix()));
nextIndex++;
}
while (matrixTasks.Count > 0)
{
try
{
var imageTask = await Task.WhenAny(matrixTasks);
matrixTasks.Remove(imageTask);
}
catch (Exception e)
{
Console.Write(1);
throw;
}
if (nextIndex < range.Count())
{
int index = nextIndex;
matrixTasks.Add(Task.Factory.StartNew(() => ComputePieceOfMatrix()));
nextIndex++;
}
}
await Task.WhenAll(matrixTasks);
}
private static void ComputePieceOfMatrix()
{
try
{
for (int j = 0; j < 10000000000; j++) ;
}
catch (Exception e)
{
Console.Write(2);
throw;
}
}
}
}
When running it from a Unit Test a have a ThreadAbortException in ComputePieceOfMatrix.
Do you have any idea ?
Edit :
According to a comment, I tried this :
static void Main(string[] args)
{
Run();
}
private static async void Run()
{
await Test();
}
public static async Task Test()
{
var range = Enumerable.Range(1, 100).ToList();
But it's exactly the same.
1.Your code causes exception
try
{
for (int j = 0; j < 10000000000; j++) ;
}
catch (Exception e)
{
Console.Write(2);
throw;
}
Just a simple OverflowException becase 10000000000 - is long and j counter int.
2.Your main tread is exiting before child threads run to finish. Most likely you are getting ThreadAbortException because Threads are closed by runtime
3.await Test() - correctly just call Test(), and await Task.WhenAny without await as well
Change the return type of Test() to Task, then wait for that Task to finish before your program reaches the end.
static void Main(string[] args)
{
Test().Wait();
}
public static async Task Test()
{
// ...
}
I would change your Test from a void to a Task return type and in the main method I would do in place of Test();
Task t = Test();
t.Wait();
I want to know when all my async threads have completed so I know when to close my loading form. My code never closes the loading form. I don't know why. I'm unsure how to correctly pass my ManualResetEvent object to the async thread too.
I'm also open to a simpler means to achieve my goal of knowing when to close the loading form.
UPDATE
After reading the advice here I've updated my class. Unfortunetly, it still does not work. I feel closer though. It's just that the callback never fires.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading.Tasks;
using System.Threading;
namespace BrianTests
{
public class TaskInfo
{
public RegisteredWaitHandle Handle;
public string OtherInfo = "default";
public Form loading;
}
public partial class AsyncControlCreateTest : Form
{
//List<ManualResetEvent> MREs = new List<ManualResetEvent>();
Form loading = new Form() { Text = "Loading...", Width = 100, Height = 100 };
CountdownWaitHandle cdwh;
public AsyncControlCreateTest()
{
InitializeComponent();
}
private void AsyncControlCreateTest_Load(object sender, EventArgs e)
{
loading.Show(this);//I want to close when all the async threads have completed
CreateControls();
}
private void CreateControls()
{
int startPoint= 0;
int threadCount = 2;
cdwh = new CountdownWaitHandle(threadCount);
for (int i = 0; i < threadCount; i++)
{
ManualResetEvent mre = new ManualResetEvent(initialState: true);
UserControl control = new UserControl() { Text = i.ToString() };
control.Load += new EventHandler(control_Load);
Controls.Add(control);
control.Top = startPoint;
startPoint += control.Height;
//MREs.Add(mre);
//mre.Set();//just set here for testing
}
Task.Factory.StartNew(new Action(() =>
{
TaskInfo info = new TaskInfo();
info.loading = loading;
try
{
info.Handle = ThreadPool.RegisterWaitForSingleObject(cdwh, WaitProc, info, 4000, executeOnlyOnce: false);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}));
}
public static void WaitProc(object state, bool timedOut)
{//this callback never occurs...
TaskInfo ti = (TaskInfo)state;
string cause = "TIMED OUT";
if (!timedOut)
{
cause = "SIGNALED";
// If the callback method executes because the WaitHandle is
// signaled, stop future execution of the callback method
// by unregistering the WaitHandle.
if (ti.Handle != null)
ti.Handle.Unregister(null);
}
Console.WriteLine("WaitProc( {0} ) executes on thread {1}; cause = {2}.",
ti.OtherInfo,
Thread.CurrentThread.GetHashCode().ToString(),
cause
);
ti.loading.Close();
}
void control_Load(object sender, EventArgs e)
{
RichTextBox newRichTextBox = new RichTextBox();
UserControl control = sender as UserControl;
control.Controls.Add(newRichTextBox);
Task.Factory.StartNew(new Action(() =>
{
Thread.Sleep(2000);
newRichTextBox.Invoke(new Action(() => newRichTextBox.Text = "loaded"));
cdwh.Signal();
}));
}
}
public class CountdownWaitHandle : WaitHandle
{
private int m_Count = 0;
private ManualResetEvent m_Event = new ManualResetEvent(false);
public CountdownWaitHandle(int initialCount)
{
m_Count = initialCount;
}
public void AddCount()
{
Interlocked.Increment(ref m_Count);
}
public void Signal()
{
if (Interlocked.Decrement(ref m_Count) == 0)
{
m_Event.Set();
}
}
public override bool WaitOne()
{
return m_Event.WaitOne();
}
}
}
The problem is that WaitHandle.WaitAll is throwing an exception, which you can see:
try
{
WaitHandle.WaitAll(MREs.ToArray());
}
catch (Exception e) {
MessageBox.Show(e.Message);
throw;
}
The error message is that "WaitAll for multiple handles on a STA thread is not supported."
If you do something like
foreach(var m in MREs)
m.WaitOne();
It will work.
I'm not quite sure why the exception did not crash the application, as I would have hoped. See perhaps How can I get WinForms to stop silently ignoring unhandled exceptions? for this.
Locking the MRE's and moving the WaitAll off the STA thread does the trick.
public partial class AsyncControlCreateTest : Form
{
object locker = new object();
static List<ManualResetEvent> MREs = new List<ManualResetEvent>();
Form loading = new Form() { Text = "Loading...", Width = 100, Height = 100 };
public AsyncControlCreateTest()
{
InitializeComponent();
}
private void AsyncControlCreateTest_Load(object sender, EventArgs e)
{
loading.Show(this);//I want to close when all the async threads have completed
CreateControls();
}
private void CreateControls()
{
int startPoint= 0;
for (int i = 0; i < 100; i++)
{
ManualResetEvent mre = new ManualResetEvent(initialState: false);
UserControl control = new UserControl() { Text = i.ToString() };
control.Load += new EventHandler(control_Load);
Controls.Add(control);
control.Top = startPoint;
startPoint += control.Height;
MREs.Add(mre);
}
Task.Factory.StartNew(new Action(() =>
{
try
{
WaitHandle.WaitAll(MREs.ToArray());
}
catch (Exception ex)
{
MessageBox.Show("error " + ex.Message);
}
finally
{
MessageBox.Show("MRE count = " + MREs.Count);//0 count provides confidence things are working...
loading.Invoke(new Action( () => loading.Close()));
}
}));
}
void control_Load(object sender, EventArgs e)
{
RichTextBox newRichTextBox = new RichTextBox();
UserControl control = sender as UserControl;
control.Controls.Add(newRichTextBox);
Task.Factory.StartNew(new Action(() =>
{
Thread.Sleep(500);
newRichTextBox.Invoke(new Action(() => newRichTextBox.Text = "loaded"));
lock (locker)
{
var ev = MREs.First();
MREs.Remove(ev);
ev.Set();
}
}));
}
}