Prioritized threads not doing their job in expected order - c#

at school I was given an assignment to build a simple application to show how threads prioritizing work. Problem is that threads I fire doesn't stick to priorities set and finish at random (or it looks like so).
Here's code for my main window:
namespace ThreadsShowtime
{
public partial class MainWindow : Window
{
private SuperThread _lowest;
private SuperThread _below;
private SuperThread _normal;
private SuperThread _above;
private SuperThread _highest;
public MainWindow()
{
InitializeComponent();
}
private void ButtonClick(object sender, RoutedEventArgs e)
{
_lowest = new SuperThread(ThreadPriority.Lowest, elipseLowest, Dispatcher, lowestLbl);
_below = new SuperThread(ThreadPriority.BelowNormal, elipseBelow, Dispatcher, belowLbl);
_normal = new SuperThread(ThreadPriority.Normal, elipseNormal, Dispatcher, normalLbl);
_above = new SuperThread(ThreadPriority.AboveNormal, elipseAbove, Dispatcher, aboveLbl);
_highest = new SuperThread(ThreadPriority.Highest, elipseHighest, Dispatcher, highestLbl);
_lowest.Start();
_below.Start();
_normal.Start();
_above.Start();
_highest.Start();
}
}
}
And SuperThread class code:
namespace ThreadsShowtime
{
public class SuperThread
{
private readonly Stopwatch _stopwatch;
public readonly Thread Thread;
public SuperThread(ThreadPriority thp, Shape sh, Dispatcher dispatcher, ContentControl lbl)
{
Thread = new Thread(() => DummyMethod(dispatcher, sh, lbl)) {Priority = thp};
_stopwatch = new Stopwatch();
}
public long Elapsed { get; private set; }
public double ElapsedSeconds => Elapsed / 1000.0;
private void DummyMethod(Dispatcher dispatcher, Shape sh, ContentControl lbl)
{
_stopwatch.Start();
dispatcher.Invoke(() =>
{
sh.Fill = Brushes.GreenYellow;
}, DispatcherPriority.Normal);
for (var idx = 0; idx < 30000; idx++) Console.WriteLine(idx);
dispatcher.Invoke(() =>
{
sh.Fill = Brushes.OrangeRed;
}, DispatcherPriority.Normal);
_stopwatch.Stop();
Elapsed = _stopwatch.ElapsedMilliseconds;
dispatcher.Invoke(() =>
{
lbl.Content = ElapsedSeconds;
}, DispatcherPriority.ContextIdle);
}
public void Start()
{
Thread.Start();
}
}
}
The output of this application looks like that:
Why does that happen?
Thank you very much for any help provided.

Because of dispatcher.Invoke() and Console.WriteLine(), your code is effectively single threaded. What you see is the slightly unpredictable results of queuing the threads. Those queues are not FIFO.
To see the proper effect, benchmark some code that is purely CPU bound, like a Sort(). Make sure they run for > 200 ms, smaller runs are meaningless.

Related

Asynchronous operations within a loop - how to keep control of execution?

Follow-on question to this one.
I am trying to generate and save a series of images. Rendering is done by Helix Toolkit, which I am told utilises the WPF composite render thread. This is causing problems because it executes asynchronously.
My original problem was that I couldn't save a given image because it hadn't yet been rendered at that time I was trying to save it. The above answer provides a workaround to this by putting the 'save' operation inside an Action which is called with low priority, thus ensuring that the rendering completes first.
This is fine for one image, but in my application I require multiple images. As it stands I cannot keep control of the sequence of events because they occur asynchronously. I am using a For loop which just continues regardless of the progress of rendering and saving the images. I need the images to be generated one by one, with enough time for rendering and saving before starting the next one.
I have tried putting delays in the loop but that causes its own problems. For instance an async await as commented in the code causes cross-threading issues because the data was created on a different thread from where the rendering is being done. I tried putting in a simple delay but then that just locks everything up - I think in part because the save operation I am waiting on has very low priority.
I cannot simply treat it as a batch of separate unrelated asynchronous tasks because I am using a single HelixViewport3D control in the GUI. The images have to be generated sequentially.
I did try a recursive method where SaveHelixPlotAsBitmap() calls DrawStuff() but that wasn't working out very well, and it doesn't seem a good approach.
I tried setting a flag ('busy') on each loop and waiting for it to be reset before continuing but that wasn't working - again, because of the asynchronous execution. Similarly I tried using a counter to keep the loop in step with the number of images that had been generated but ran into similar problems.
I seem to be going down a rabbit hole of threading and asynchronous operations that I don't want to be in.
How can I resolve this?
class Foo {
public List<Point3D> points;
public Color PointColor;
public Foo(Color col) { // constructor creates three arbitrary 3D points
points = new List<Point3D>() { new Point3D(0, 0, 0), new Point3D(1, 0, 0), new Point3D(0, 0, 1) };
PointColor = col;
}
}
public partial class MainWindow : Window
{
int i = -1; // counter
public MainWindow()
{
InitializeComponent();
}
private void Go_Click(object sender, RoutedEventArgs e) // STARTING POINT
{
// Create list of objects each with three 3D points...
List<Foo> bar = new List<Foo>(){ new Foo(Colors.Red), new Foo(Colors.Green), new Foo(Colors.Blue) };
foreach (Foo b in bar)
{
i++;
DrawStuff(b, SaveHelixPlotAsBitmap); // plot to helixViewport3D control ('points' = list of 3D points)
// This is fine the first time but then it runs away with itself because the rendering and image grabbing
// are asynchronous. I need to keep it sequential i.e.
// Render image 1 -> save image 1
// Render image 2 -> save image 2
// Etc.
}
}
private void DrawStuff(Foo thisFoo, Action renderingCompleted)
{
//await System.Threading.Tasks.Task.Run(() =>
//{
Point3DCollection dataList = new Point3DCollection();
PointsVisual3D cloudPoints = new PointsVisual3D { Color = thisFoo.PointColor, Size = 5.0f };
foreach (Point3D p in thisFoo.points)
{
dataList.Add(p);
}
cloudPoints.Points = dataList;
// Add geometry to helixPlot. It renders asynchronously in the WPF composite render thread...
helixViewport3D.Children.Add(cloudPoints);
helixViewport3D.CameraController.ZoomExtents();
// Save image (low priority means rendering finishes first, which is critical)..
Dispatcher.BeginInvoke(renderingCompleted, DispatcherPriority.ContextIdle);
//});
}
private void SaveHelixPlotAsBitmap()
{
Viewport3DHelper.SaveBitmap(helixViewport3D.Viewport, $#"E:\test{i}.png", null, 4, BitmapExporter.OutputFormat.Png);
}
}
Note These examples are just to prove a concept, there is work needed on the TaskCompletionSource to handle errors
Given this test window
<Window x:Class="WpfApp2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<StackPanel x:Name="StackPanel"/>
</Grid>
</Window>
Here is an example of how to use events to know when the view is in the state that you want.
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
namespace WpfApp2
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DoWorkAsync();
}
private async Task DoWorkAsync()
{
for (int i = 0; i < 10; i++)
{
await RenderAndCapture();
}
}
private async Task RenderAndCapture()
{
await RenderAsync();
CaptureScreen();
}
private Task RenderAsync()
{
var taskCompletionSource = new TaskCompletionSource<object>();
Dispatcher.Invoke(() =>
{
var panel = new TextBlock {Text = "NewBlock"};
panel.Loaded += OnPanelOnLoaded;
StackPanel.Children.Add(panel);
void OnPanelOnLoaded(object sender, RoutedEventArgs args)
{
panel.Loaded -= OnPanelOnLoaded;
taskCompletionSource.TrySetResult(null);
}
});
return taskCompletionSource.Task;
}
private void CaptureScreen()
{
// Capture Image
}
}
}
If you want to have your sync method called from outside you can implement a task queue.
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
namespace WpfApp2
{
public class TaskQueue
{
private readonly SemaphoreSlim _semaphore;
public TaskQueue()
{
_semaphore = new SemaphoreSlim(1);
}
public async Task Enqueue(Func<Task> taskFactory)
{
await _semaphore.WaitAsync();
try
{
await taskFactory();
}
finally
{
_semaphore.Release();
}
}
}
public partial class MainWindow : Window
{
private readonly TaskQueue _taskQueue;
public MainWindow()
{
_taskQueue = new TaskQueue();
InitializeComponent();
DoWork();
}
private void DoWork()
{
for (int i = 0; i < 10; i++)
{
QueueRenderAndCapture();
}
}
private void QueueRenderAndCapture()
{
_taskQueue.Enqueue(() => RenderAndCapture());
}
private async Task RenderAndCapture()
{
await RenderAsync();
CaptureScreen();
}
private Task RenderAsync()
{
var taskCompletionSource = new TaskCompletionSource<object>();
Dispatcher.Invoke(() =>
{
var panel = new TextBlock {Text = "NewBlock"};
panel.Loaded += OnPanelOnLoaded;
StackPanel.Children.Add(panel);
void OnPanelOnLoaded(object sender, RoutedEventArgs args)
{
panel.Loaded -= OnPanelOnLoaded;
taskCompletionSource.TrySetResult(null);
}
});
return taskCompletionSource.Task;
}
private void CaptureScreen()
{
// Capture Screenshot
}
}
}
This will make sure the UI is in the state required for each iteration
You will of course need to expand this so that you listen to the Loaded event of each point that you wish to render.
Edit:
As PointsVisual3D does not have the Loaded event you can complete the task by hooking onto the event you had previously used. Not ideal, but it should work.
private Task RenderAsync()
{
var taskCompletionSource = new TaskCompletionSource<object>();
Dispatcher.Invoke(() =>
{
var panel = new TextBlock {Text = "NewBlock"};
StackPanel.Children.Add(panel);
Dispatcher.BeginInvoke(new Action(() =>
{
taskCompletionSource.TrySetResult(null);
}), DispatcherPriority.ContextIdle);
});
return taskCompletionSource.Task;
}
Solution below. This is my implementation of the code provided in Jason's answer. All credit to Jason for the important bits.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Go_Click(object sender, RoutedEventArgs e) // STARTING POINT
{
DoWorkAsync();
}
private async Task DoWorkAsync()
{
// Create list of objects each with three 3D points...
List<Foo> bar = new List<Foo>() { new Foo(Colors.Red), new Foo(Colors.Green), new Foo(Colors.Blue) };
int i = -1; // init counter
foreach (Foo b in bar)
{
i++;
await RenderAndCapture(b, i);
}
}
private async Task RenderAndCapture(Foo b, int i)
{
await RenderAsync(b);
SaveHelixPlotAsBitmap(i);
}
private Task RenderAsync(Foo b)
{
var taskCompletionSource = new TaskCompletionSource<object>();
Dispatcher.Invoke(() =>
{
DrawStuff(b);
Dispatcher.BeginInvoke(new Action(() =>
{
taskCompletionSource.TrySetResult(null);
}), DispatcherPriority.ContextIdle);
});
return taskCompletionSource.Task;
}
private void DrawStuff(Foo thisFoo)
{
Point3DCollection dataList = new Point3DCollection();
PointsVisual3D cloudPoints = new PointsVisual3D { Color = thisFoo.PointColor, Size = 5.0f };
foreach (Point3D p in thisFoo.points)
{
dataList.Add(p);
}
cloudPoints.Points = dataList;
// Add geometry to helixPlot. It renders asynchronously in the WPF composite render thread...
helixPlot.Children.Add(cloudPoints);
helixPlot.CameraController.ZoomExtents();
}
private void SaveHelixPlotAsBitmap(int i) // screenshot
{
Viewport3DHelper.SaveBitmap(helixPlot.Viewport, $#"E:\test{i}.png", null, 4, BitmapExporter.OutputFormat.Png);
}
}

WinForm asynchronously update UI status from console application call

I want to asynchronously update UI status when doing a long-time task . The program is a console application , however , when I execute the async operations , the UI thread will exit soon after the task begins .
How should I let the UI thread wait when my long-time task finish ?
I simplify my code as below :
public static class Program
{
static void Main()
{
WorkerWrapper wp = new WorkerWrapper();
wp.ProcessData();
}
}
public class WorkerWrapper
{
private RateBar bar;
public void ProcessData()
{
bar = new RateBar();
bar.Show();
Worker wk = new Worker();
wk.WorkProcess += wk_WorkProcess;
Action handler = new Action(wk.DoWork);
var result = handler.BeginInvoke(new AsyncCallback(this.AsyncCallback), handler);
}
private void AsyncCallback(IAsyncResult ar)
{
Action handler = ar.AsyncState as Action;
handler.EndInvoke(ar);
}
private void wk_WorkProcess(object sender, PrecentArgs e)
{
if (e.Precent < 100)
{
bar.Precent = e.Precent;
}
}
}
public class Worker
{
public event EventHandler<PrecentArgs> WorkProcess;
public void DoWork()
{
for (int i = 0; i < 100; i++)
{
WorkProcess(this, new PrecentArgs(i));
Thread.Sleep(100);
}
}
}
public class PrecentArgs : EventArgs
{
public int Precent { get; set; }
public PrecentArgs(int precent)
{
Precent = precent;
}
}
public partial class RateBar : Form
{
public int Precent
{
set
{
System.Windows.Forms.MethodInvoker invoker = () => this.progressBar1.Value = value;
if (this.progressBar1.InvokeRequired)
{
this.progressBar1.Invoke(invoker);
}
else
{
invoker();
}
}
}
public RateBar()
{
InitializeComponent();
}
}
However , in method ProcessData() , if I add result.AsyncWaitHandle.WaitOne() in the end to wait my operation to complete , the Form will freeze .
Is there anything wrong with my way to wait the thread to complete ?
Reason that your application exiting before your "background threads" completed is when there are multiple threads application exists soon after there are not any foreground threads. This is explained more in here http://msdn.microsoft.com/en-us/library/system.threading.thread.isbackground(v=vs.110).aspx
You should add proper waiting mechanisms to your background threads to be completed. There are multiple ways of letting other threads know that the thread is complete. Please refer here. How to wait for thread to finish with .NET?
You shouldn't block the UI thread waiting for the result, but rather retrieve the result from EndInvoke. Your deadlock probably occurs because you are using both result.AsyncWaitHandle.WaitOne() and EndInvoke, both will block until the result is available.
In my opinion the best option is to not call result.AsyncWaitHandle.WaitOne() and just retrieve the result in the AsyncCallback
private void AsyncCallback(IAsyncResult ar)
{
Action handler = ar.AsyncState as Action;
var result = handler.EndInvoke(ar);
}
More information here. Also if you are using .net 4.0 or higher, this sort of thing can be done much easier with async/await.
I write down this solution and hope it may helps others with same question .
The key to this problem is to use a new thread to run RateBar's ShowDialog function .
public void ProcessData()
{
new Thread(() => new RateBar().ShowDialog()).Start();
Worker wk = new Worker();
wk.WorkProcess += wk_WorkProcess;
Action handler = new Action(wk.DoWork);
var result = handler.BeginInvoke(new AsyncCallback(this.AsyncCallback), handler);
}

C# Producer/Consumer implementation issue

I have a producer-consumer class as following.
public class ProducerConsumer<T> where T : class
{
private Thread _workerThread;
private readonly Queue<T> _workQueue;
private readonly object _lockObject = new object();
private readonly Action<T> _workCallbackAction;
private ManualResetEvent _workerWaitSignal;
public ProducerConsumer(Action<T> action)
{
_workCallbackAction = action;
_workQueue = new Queue<T>();
}
private void DoWork()
{
while (true)
{
T workItemToBeProcessed = default(T);
bool hasSomeWorkItem = false;
lock (_lockObject)
{
hasSomeWorkItem = _workQueue.Count > 0;
if (hasSomeWorkItem)
{
workItemToBeProcessed = _workQueue.Dequeue();
if (workItemToBeProcessed == null)
{
return;
}
}
}
if (hasSomeWorkItem)
{
if (_workCallbackAction != null)
{
_workCallbackAction(workItemToBeProcessed);
}
}
else
{
_workerWaitSignal.WaitOne();
Debug.WriteLine("Waiting for signal.");
}
}
}
public void EnQueueWorkItem(T workItem)
{
lock (_lockObject)
{
_workQueue.Enqueue(workItem);
_workerWaitSignal.Set();
}
}
public void StopWork(ManualResetEvent stopSignal)
{
EnQueueWorkItem(null);
_workerThread.Join();
_workerWaitSignal.Close();
_workerWaitSignal = null;
if (stopSignal != null)
{
stopSignal.Set();
}
}
public void ReStart()
{
_workerWaitSignal = new ManualResetEvent(false);
_workerThread = new Thread(DoWork) { IsBackground = true };
_workerThread.Start();
}
}
and i am using this in the following way:
public partial class Form1 : Form
{
private RecordProducerConsumer<string> _proConsumer;
public Form1()
{
InitializeComponent();
_proConsumer = new RecordProducerConsumer<string>(DoAction);
}
private bool restart=true;
private int item = 0;
private void button1_Click(object sender, EventArgs e)
{
if (restart)
{
_proConsumer.ReStart();
restart = false;
}
item++;
_proConsumer.EnQueueWorkItem(item.ToString());
}
private void DoAction(string str)
{
Debug.WriteLine(str);
}
private void btnStop_Click(object sender, EventArgs e)
{
ManualResetEvent mre = new ManualResetEvent(false);
_proConsumer.StopWork(mre);
mre.WaitOne();
restart = true;
}
private void Stop(ManualResetEvent mre)
{
mre.WaitOne();
}
}
My problem or what I can not understand is when I click Start button I am adding only one item and it Dequeue that item but keeps on running the loop so I see lot of "Waiting for signal." print outs on the Output window in Visual Studio.
Why does it not stop on _workerWaitSignal.WaitOne(); in DoWork() method , why is it running all the time ?
Couple of issues:
It makes hardly any sense to output 'Waiting for issue' after executing the wait. Consider moving the write before the actual wait.
You are using a ManualResetEvent — as its name indicates, it requires a manual reset to revert from the signalled state. However, I can't see a call to Reset in your code.
To avoid other concurrency issues (e.g. a race condition when when resetting the event while other thread set the event after enqueuing another work item), consider using a Semaphore for your scenario instead.
try this... I could be wrong...but that is all i could figure out by reading through your code. Hope this helps :)
private void button1_Click(object sender, EventArgs e)
{
if (restart)
{
restart = false;
_proConsumer.ReStart();
}
item++;
_proConsumer.EnQueueWorkItem(item.ToString());
}
I haven't read the code thoroughly, but I can venture a guess that you meant to use an AutoResetEvent (which resets automatically after some WaitOne() is released) rather than a ManualResetEvent (which stays set until you explicitly call Reset()).
Also, is there any reason you're not using .NET's BlockingCollection<T> ? It's the framework implementation of the producer/consumer pattern, and it works very well.

How does one kick off a timed sequence of events on the GUI thread in C#?

I've got an app that has to do the following type of things, preferably on the GUI thread since that's where most of the action is taking place and there's no long-running ops:
Wait 1000
FuncA()
Wait 2000
FuncB()
Wait 1000
FuncC()
I realize I could use a timer with a state-machine style OnTick function, but that seems cumbersome:
int _state;
void OnTick(object sender, EventArgs e) {
switch (_state) {
case 0:
FuncA();
_timer.Interval = TimeSpan.FromSeconds(2);
_state = 1;
break;
case 1:
FuncB();
_timer.Interval = TimeSpan.FromSeconds(1);
_state = 2;
break;
case 2:
FuncC();
_timer.IsEnabled = false;
_state = 0;
}
}
Plus I'd like to be able to make it generic enough to do something like
RunSequenceOnGuiThread(new Sequence {
{1000, FuncA}
{2000, FuncB}
{1000, FuncC}};
Is there an idiomatic way to do this kind of thing? Given all the TPL stuff, or Rx, or even the computation expressions in F# I'd assume one exists, but I'm not finding it.
Observable.Concat(
Observer.Timer(1000).Select(_ => Func1()),
Observer.Timer(2000).Select(_ => Func2()),
Observer.Timer(1000).Select(_ => Func3()))
.Repeat()
.Subscribe();
The only thing you have to do to make this work, is make sure that your Func's return a value (even if that value is Unit.Default, i.e. nothing)
Edit: Here's how to make a generic version:
IObservable<Unit> CreateRepeatingTimerSequence(IEnumerable<Tuple<int, Func<Unit>>> actions)
{
return Observable.Concat(
actions.Select(x =>
Observable.Timer(x.Item1).Select(_ => x.Item2())))
.Repeat();
}
Here's a sketch of this in F#:
let f() = printfn "f"
let g() = printfn "g"
let h() = printfn "h"
let ops = [
1000, f
2000, g
1000, h
]
let runOps ops =
async {
for time, op in ops do
do! Async.Sleep(time)
op()
} |> Async.StartImmediate
runOps ops
System.Console.ReadKey() |> ignore
That's in a console app, but you can just call runOps on the GUI thread. See also this blog.
If you're using VS11/NetFx45/C#5, you can do a similar thing with C# async/await and a List of Tuple of Action delegates.
using the async CTP or .NET 4.5 (C# 5) it's REALLY easy using an async method and the await operator. This can be called directly on the UI thread and it will work as expected.
public async void ExecuteStuff()
{
await TaskEx.Delay(1000);
FuncA();
await TaskEx.Delay(2000);
FuncB();
await TaskEx.Delay(1000);
FuncC();
}
Here's a way to combine "yield return" and the reactive framework to give you a "poor man's async". Basically lets you "await" any IObservable. Here I just use it for timers since that's what you were interested in, but it you can have it "await" button clicks (using a Subject<Unit>) etc before moving on to the next thing as well.
public sealed partial class Form1 : Form {
readonly Executor _executor = new Executor();
public Form1() {
InitializeComponent();
_executor.Run(CreateAsyncHandler());
}
IEnumerable<IObservable<Unit>> CreateAsyncHandler() {
while (true) {
var i = 0;
Text = (++i).ToString();
yield return WaitTimer(500);
Text = (++i).ToString();
yield return WaitTimer(500);
Text = (++i).ToString();
yield return WaitTimer(500);
Text = (++i).ToString();
}
}
IObservable<Unit> WaitTimer(double ms) {
return Observable.Timer(TimeSpan.FromMilliseconds(ms), new ControlScheduler(this)).Select(_ => Unit.Default);
}
}
public sealed class Executor {
IEnumerator<IObservable<Unit>> _observables;
IDisposable _subscription = new NullDisposable();
public void Run(IEnumerable<IObservable<Unit>> actions) {
_observables = (actions ?? new IObservable<Unit>[0]).Concat(new[] {Observable.Never<Unit>()}).GetEnumerator();
Continue();
}
void Continue() {
_subscription.Dispose();
_observables.MoveNext();
_subscription = _observables.Current.Subscribe(_ => Continue());
}
public void Stop() {
Run(null);
}
}
sealed class NullDisposable : IDisposable {
public void Dispose() {}
}
It's a slight modification of Daniel Earwicker's AsyncIOPipe idea: http://smellegantcode.wordpress.com/2008/12/05/asynchronous-sockets-with-yield-return-of-lambdas/
Interesting all the different responses. Here's a simple DIY option that doesn't depend on any other libraries, and doesn't hog thread resources unnecessarily.
Basically, for each action in your list, it creates an onTick function that executes that action, then recursively calls DoThings with the remaining actions and delays.
Here, ITimer is just a simple wrapper around DispatcherTimer (but it would work with a SWF Timer as well, or a mock timer for unit testing), and DelayedAction is just a Tuple with int Delay and Action action
public static class TimerEx {
public static void DoThings(this ITimer timer, IEnumerable<DelayedAction> actions) {
timer.DoThings(actions.GetEnumerator());
}
static void DoThings(this ITimer timer, IEnumerator<DelayedAction> actions) {
if (!actions.MoveNext())
return;
var first = actions.Current;
Action onTick = null;
onTick = () => {
timer.IsEnabled = false;
first.Action();
// ReSharper disable AccessToModifiedClosure
timer.Tick -= onTick;
// ReSharper restore AccessToModifiedClosure
onTick = null;
timer.DoThings(actions);
};
timer.Tick += onTick;
timer.Interval = first.Delay;
timer.IsEnabled = true;
}
}
If you don't want to delve into F# or reference Rx or use .Net 4.5 this is a simple viable solution.
Here's an example of how to test it:
[TestClass]
public sealed class TimerExTest {
[TestMethod]
public void Delayed_actions_should_be_scheduled_correctly() {
var timer = new MockTimer();
var i = 0;
var action = new DelayedAction(0, () => ++i);
timer.DoThings(new[] {action, action});
Assert.AreEqual(0, i);
timer.OnTick();
Assert.AreEqual(1, i);
timer.OnTick();
Assert.AreEqual(2, i);
timer.OnTick();
Assert.AreEqual(2, i);
}
}
And here's the other classes to make it compile:
public interface ITimer {
bool IsEnabled { set; }
double Interval { set; }
event Action Tick;
}
public sealed class Timer : ITimer {
readonly DispatcherTimer _timer;
public Timer() {
_timer = new DispatcherTimer();
_timer.Tick += (sender, e) => OnTick();
}
public double Interval {
set { _timer.Interval = TimeSpan.FromMilliseconds(value); }
}
public event Action Tick;
public bool IsEnabled {
set { _timer.IsEnabled = value; }
}
void OnTick() {
var handler = Tick;
if (handler != null) {
handler();
}
}
}
public sealed class MockTimer : ITimer {
public event Action Tick;
public bool IsEnabled { private get; set; }
public double Interval { set { } }
public void OnTick() {
if (IsEnabled) {
var handler = Tick;
if (handler != null) {
handler();
}
}
}
}
public sealed class DelayedAction {
readonly Action _action;
readonly int _delay;
public DelayedAction(int delay, Action action) {
_delay = delay;
_action = action;
}
public Action Action {
get { return _action; }
}
public int Delay {
get { return _delay; }
}
}
If you can use the C# 4.5 to do it, go with Firoso post: it's the best way accomplish that in C#, exactly what Async was built for.
However, if you can't, there might be some ways to do it. I'd do a "simple" manager to do it:
public partial class Form1 : Form
{
private TimedEventsManager _timedEventsManager;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
_timedEventsManager
= new TimedEventsManager(this,
new TimedEvent(1000, () => textBox1.Text += "First\n"),
new TimedEvent(5000, () => textBox1.Text += "Second\n"),
new TimedEvent(2000, () => textBox1.Text += "Third\n")
);
}
private void button1_Click(object sender, EventArgs e)
{
_timedEventsManager.Start();
}
}
public class TimedEvent
{
public int Interval { get; set; }
public Action Action { get; set; }
public TimedEvent(int interval, Action func)
{
Interval = interval;
Action = func;
}
}
public class TimedEventsManager
{
private readonly Control _control;
private readonly Action _chain;
public TimedEventsManager(Control control, params TimedEvent[] timedEvents)
{
_control = control;
Action current = null;
// Create a method chain, beginning by the last and attaching it
// the previous.
for (var i = timedEvents.Length - 1; i >= 0; i--)
{
var i1 = i;
var next = current;
current = () =>
{
Thread.Sleep(timedEvents[i1].Interval);
// MUST run it on the UI thread!
_control.Invoke(new Action(() => timedEvents[i1].Action()));
if (next != null) next();
};
}
_chain = current;
}
public void Start()
{
new Thread(new ThreadStart(_chain)).Start();
}
}
Beware that this example is Winforms specific (uses Control.Invoke()). You will need a slightly different version for WPF, which uses the thread dispatcher to achieve the same thing. (if my memory doesn't fail me, you also can use Control.Dispatcher.Invoke(), but keep in mind that it is a different control)

C# WinForms MultiThreading in Loop

Scenario
I have a background worker in my application that runs off and does a bunch of processing. I specifically used this implementation so as to keep my User Interface fluid and prevent it from freezing up. I want to keep the background worker, but inside that thread, spawn off ONLY 3 MORE threads - making them share the processing (currently the worker thread just loops through and processes each asset one-by-one. However I would like to speed this up but using only a limited number of threads.
Question
Given the code below, how can I get the loop to choose a thread that is free, and then essentially wait if there isn't one free before it continues.
CODE
foreach (KeyValuePair<int, LiveAsset> kvp in laToHaganise)
{
Haganise h = new Haganise(kvp.Value,
busDate,
inputMktSet,
outputMktSet,
prodType,
noOfAssets,
bulkSaving);
h.DoWork();
}
Thoughts
I'm guessing that I would have to start off by creating 3 new threads, but my concern is that if I'm instantiating a new Haganise object each time - how can I pass the correct "h" object to the correct thread.....
Thread firstThread = new Thread(new ThreadStart(h.DoWork));
Thread secondThread =new Thread(new ThreadStart(h.DoWork));
Thread thirdThread = new Thread(new ThreadStart(h.DoWork));
Help greatly appreciated.
Depending on the size of the job the thread has to do, ThreadPool.QueueUserWorkItem may be a good option. That will create a thread and put it in a queue and execute it when there is thread in the pool available.
Alternatively you could create your own type of Pool which has 3 threads and use ManualResetEvent to determine when a particular thread is finished.
Two possibilities:
Use the ThreadPool.QueueUserWorkItem (as already mentioned by James)
Implement it BackgroundWorkers on yourself (see below)
Here is a working example, that selects the next free worker:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;
namespace ConsoleApplication1
{
class Program
{
private static List<MyWorker> _Workers;
static void Main(string[] args)
{
_Workers = new List<MyWorker>();
for (int i = 0; i < 5; i++)
{
_Workers.Add(CreateDefaultWorker(i));
}
StartJobs(20000);
Console.ReadKey();
}
private static void StartJobs(int runtime)
{
Random rand = new Random();
DateTime startTime = DateTime.Now;
while (DateTime.Now - startTime < TimeSpan.FromMilliseconds(runtime))
{
var freeWorker = GetFreeWorker();
if (freeWorker != null)
{
freeWorker.Worker.RunWorkerAsync(new Action(() => DoSomething(freeWorker.Index, rand.Next(500, 2000))));
}
else
{
Console.WriteLine("No free worker available!");
Console.WriteLine("Waiting for free one...");
WaitForFreeOne();
}
}
}
private static MyWorker GetFreeWorker()
{
foreach (var worker in _Workers)
{
if (!worker.Worker.IsBusy)
return worker;
}
return null;
}
private static void WaitForFreeOne()
{
while (true)
{
foreach (var worker in _Workers)
{
if (!worker.Worker.IsBusy)
return;
}
Thread.Sleep(1);
}
}
private static MyWorker CreateDefaultWorker(int index)
{
var worker = new MyWorker(index);
worker.Worker.DoWork += (sender, e) => ((Action)e.Argument).Invoke();
worker.Worker.RunWorkerCompleted += (sender, e) => Console.WriteLine("Job finished in worker " + worker.Index);
return worker;
}
static void DoSomething(int index, int timeout)
{
Console.WriteLine("Worker {1} starts to work for {0} ms", timeout, index);
Thread.Sleep(timeout);
}
}
public class MyWorker
{
public int Index { get; private set; }
public BackgroundWorker Worker { get; private set; }
public MyWorker(int index)
{
Index = index;
Worker = new BackgroundWorker();
}
}
}

Categories

Resources