I have a thread that adds points onto a zedgraph component on a certain time interval. I need to pause the adding of the points on pressing a check box and then resume adding them when the checkbox is pressed again. The following is what I have for thread:
public class myThread{
ManualResetEvent pauseResumeThread = new ManualResetEvent(true);
public void threadHeartrateGraph()
{
for (int k = 15; k < _hr.Length; k++)
{
pauseResumeThread.WaitOne();
if (HRDataSummary.threadPause == true)
{
break;
}
x = k;
y = _hr[k];
list1.Add(x, y);
_displayHRGraph.Invoke(list1, graph_HeartRate, _GraphName[0]);
graph_HeartRate.XAxis.Scale.Min = k-14;
graph_HeartRate.XAxis.Scale.Max = k+1;
Thread.Sleep(_interval * 1000);
}
}
catch (NullReferenceException)
{
}
}
public void play()
{
pauseResumeThread.Set();
}
public void pause()
{
pauseResumeThread.Reset();
}
}
And then, I have called for the play and pause thread from a checkbox.
private void checkBoxPause_CheckedChanged(object sender, EventArgs e)
{
if(checkBoxPause.Checked == true)
{
//HRDataSummary.threadPause = true;
checkBoxPause.Text = "Play >";
myThread mythread = new myThread();
Thread pause = new Thread(mythread.pause);
pause.Start();
}
if (checkBoxPause.Checked == false)
{
//HRDataSummary.threadPause = false;
checkBoxPause.Text = "Pause ||";
myThread mythread = new myThread();
Thread play = new Thread(mythread.play);
play.Start();
}
}
What am I missing? Or is it completely wrong use of ManualResetEvent?
First you declare your myThread Object globally (only one!).
myThread heartGraph = new myThread()
Then you want to start your Worker-Method in a new Thread.
Thread worker = new Thread(heartGraph.threadHeartrateGraph);
worker.Start();
Now you can use your ManualResetEvent to Pause/Resume the work.
if (checkBoxPause.Checked == true) {
//HRDataSummary.threadPause = true;
checkBoxPause.Text = "Play >";
heartGraph.pause();
} else {
//HRDataSummary.threadPause = false;
checkBoxPause.Text = "Pause ||";
heartGraph.play();
}
Related
When I start program, I need to load some projects and init them. So I make that all in Thread and create a progress bar (next PB). When loading is started everythink is OK, but in one moment PB is stop and jump to end when init will end.
Here Thread which add to progress bar every init bLock.
public void initMapBlocks(int n, int m)
{
this.Dispatcher.Invoke(delegate ()
{
Loader.Visibility = Visibility.Visible;
mainWindow.Visibility = Visibility.Hidden;
});
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
this.Dispatcher.Invoke(delegate ()
{
MapPart mp = new MapPart();
mainCanvas.Children.Add(mp);
Loader.addPoint(1);
});
}
}
this.Dispatcher.Invoke(delegate ()
{
Loader.Visibility = Visibility.Hidden;
mainWindow.Visibility = Visibility.Visible;
});
}
Did it`s possible to get full animation?
-------- Update --------
Here code where i add to progress bar value.
public void addPoint(int x)
{
itemCountAlready += x;
loaderBar.Value += x;
}
----- Update 2 -------
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Thread thread = new Thread(new ThreadStart(() => initMapBlocks(100, 100)));
thread.Start();
}
Your initMapBlocks method produces (MapPart) and consumes (adds loader's value and mainCanvas children) at the same time. You can separate by using System.Collections.Generic.Queue
You can define your queue like this
private Queue<MapPart> myQueue = new Queue<MapPart>();
Instead of these guys
mainCanvas.Children.Add(mp);
Loader.addPoint(1);
You can write simply this for produce
myQueue.Enqueue(mp);
And you can define a DispatcherTimer in your constructor for consume
DispatcherTimer myTimer = new DispatcherTimer {
Interval = TimeSpan.FromMilliseconds(1)
};
myTimer.Tick += Consume;
myTimer.Start();
Your Consume method
private void Consume(object sender, EventArgs args) {
if (myQueue.Count > 0) {
//you don't need `this.Dispatcher.Invoke(delegate...` too
loaderBar.Value += myQueue.Dequeue();
mainCanvas.Children.Add(mp);
}
}
Hi there i am trying to make a question answer game. ( Whoever presses joystick button first answers the question first )
So far i ' ve used SharpDX , threading since i cant handle the button press event via SharpDX
My problem is i cant get it worked . I dont know the reason but let me explain my code.
On my page load i ' ve initialized 2 different threads ;
//Initializing the constants
private bool q = true;
private Thread th1;
private Thread th2;
private Guid joy1Guid = Guid.Empty;
private Guid joy2Guid = Guid.Empty;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
var directInput = new DirectInput();
foreach (var deviceInstance in directInput.GetDevices())
{
if (deviceInstance.Subtype == 258)
{
//My joysticks connected to pc via usb.
if (joy1Guid == Guid.Empty)
joy1Guid = deviceInstance.InstanceGuid;
else if (joy2Guid == Guid.Empty)
joy2Guid = deviceInstance.InstanceGuid;
}
}
th1 = new Thread(Joy1Start);
th2 = new Thread(Joy2Start);
th1.IsBackground = true;
th2.IsBackground = true;
}
Below my Thread methods
private void Joy1Start()
{
var directInput = new DirectInput();
var joystick = new Joystick(directInput, joy1Guid);
joystick.Properties.BufferSize = 128;
joystick.Acquire();
while (q)
{
joystick.Poll();
var state = joystick.GetCurrentState();
var counter = 0;
foreach (bool isPressed in state.Buttons)
{
if (isPressed)
{
Action a1 = () => textBox1.Text = "JOY1";
textBox1.Invoke(a1);
th1.Abort();
th2.Abort();
q = false;
break;
}
counter++;
}
}
}
private void Joy2Start()
{
var directInput = new DirectInput();
var joystick = new Joystick(directInput, joy2Guid);
joystick.Properties.BufferSize = 128;
joystick.Acquire();
while (q)
{
joystick.Poll();
var state = joystick.GetCurrentState();
var counter = 0;
foreach (bool isPressed in state.Buttons)
{
if (isPressed)
{
Action a2 = () => textBox1.Text = "JOY2";
textBox1.Invoke(a2);
th1.Abort();
q = false;
break;
}
counter++;
}
}
}
I am firing thread starts with a button
private void button1_Click(object sender, EventArgs e)
{
th1.Start();
th2.Start();
}
So the problem is i can't get any response if i work with two threads.
When i only start th1 or th2 it works like a charm.
Can you please show me where is the wrong part of this algorithm ?
I've an "animateMyWindow" class to change opened window's opacity with Timer.
namespace POCentury
{
class animateMyWindow
{
Timer _timer1 = new Timer();
Window _openedWindow = null;
public void animationTimerStart(object openedWindow)
{
if (openedWindow == null)
{
throw new Exception("Hata");
}
else
{
_openedWindow = (Window)openedWindow;
_timer1.Interval = 1 * 25;
_timer1.Elapsed += new ElapsedEventHandler(animationStart);
_timer1.AutoReset = true;
_timer1.Enabled = true;
_timer1.Start();
}
}
private void animationStart(object sender, ElapsedEventArgs e)
{
if (_openedWindow.Opacity == 1)
animationStop();
else
_openedWindow.Opacity += .1;
}
private void animationStop()
{
_timer1.Stop();
}
}
}
animationStart function can't reach my window because it is working on a different thread.
I've tried Dispatcher.BeginInvoke and can't make it work.
Can you help me with doing that?
Basically, you can't access the openedWindow inside the animationStart event because it's happening in a different thread. You need the Dispatcher to do that.
Dispatcher.BeginInvoke(new Action(() =>
{
if (_openedWindow.Opacity == 1)
animationStop();
else
_openedWindow.Opacity += .1;
}));
I need to freeze the main thread until the end recursion.
Recursion depth = count of threads.
sample code:
BackgroundWorker backgroundWorker1;
Random ran;
private void Form1_Load(object sender, EventArgs e)
{
method();
label1.Text = "Threads is finished";
}
private void method() // recursive method
{
Thread.Sleep(100);
backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.DoWork +=
new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerAsync(); //Beginning new thread
}
private void backgroundWorker1_DoWork(object sender,
DoWorkEventArgs e)
{
ran = new Random();
Thread.Sleep(ran.Next(500, 1000));
if (ran.Next(1, 5) != 1) // if = 1 then to stop recursion
{
method();
}
}
When the threads are completed, label1.Text must have the value "Threads is finished". How is this done?
Console application PoC which caches references to all created workers and uses numeric variable to check how many workers still in progress, when this value reaches 0 - application terminates. Please let me know in case of any questions.
class Program
{
private static IList<BackgroundWorker> workers;
private static Random ran;
private static int activeWorkersCount;
static void Main(string[] args)
{
workers = new List<BackgroundWorker>();
DoWork();
while (activeWorkersCount > 0)
{
Thread.Sleep(200);
}
Console.WriteLine("Waiting for all workers to finish...");
Console.ReadLine();
}
private static void DoWork() // recursive method
{
Thread.Sleep(100);
var newWorker = new BackgroundWorker();
newWorker.DoWork += BackgroundWorkerDoWork;
newWorker.RunWorkerCompleted += (o, e) =>
{
Console.WriteLine("[E] Worker finished");
Interlocked.Decrement(ref activeWorkersCount);
};
Interlocked.Increment(ref activeWorkersCount);
newWorker.RunWorkerAsync();
}
private static void BackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
{
Console.WriteLine("[S] Worker started");
ran = new Random();
Thread.Sleep(ran.Next(500, 1000));
if (ran.Next(1, 5) != 1) // if = 1 then to stop recursion
{
DoWork();
}
}
}
When you decide it's time to stop recursion you can post a callback onto the GUI thread. Off the top of my head it'll be something like this:
if(ran.Next(1,5)!=1)
{
method();
}
else
{
Action action=()=>label1.Text = "Threads is finished";
this.BeginInvoke(action);
}
1) Create a method to update Label1:
private void WorkFinished()
{
if(Label1.InvokeRequired)
{
Label1.Invoke(WorkFinished);
}
else
{
Label1.Text = "Threads is finished";
}
}
2) Call WorkFinished() from backgroundWorker1_DoWork.
You can use CountDownEvent class:
Example:
using (CountDownEvent countdownEvent = new CountdownEvent(numberOfThreads))
{
for (int threadIndex= 0; i < numberOfThreads; threadIndex++)
ThreadPool.QueueUserWorkItem(
th =>
{
DoWork();
countdownEvent.Signal();
}, threadIndex);
countdownEvent.Wait();
}
Console.WriteLine("All threads complete");
I did it using a volatile integer number and it works well.
BackgroundWorker backgroundWorker1;
Random ran;
long runningThreads = 0;
public void Start()
{
method();
// Console.WriteLine("Threads is finished");
}
private void method() // recursive method
{
Interlocked.Increment(ref runningThreads);
Console.WriteLine("New thread started");
Thread.Sleep(100);
backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.DoWork +=
new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerAsync(); //Beginning new thread
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
ran = new Random();
Thread.Sleep(ran.Next(500, 1000));
if (ran.Next(1, 5) != 1) // if = 1 then to stop recursion
{
method();
}
Finished();
}
private void Finished()
{
Interlocked.Decrement(ref runningThreads);
if (Interlocked.Read(ref runningThreads) == 0)
{
Console.WriteLine("Threads is finished");
}
}
In the following code example, I want to change the color of the Foreground text of a TextBox from my BackgroundThread, but it gets the error:
This thread cannot access this object
since it is in another thread.
What do I have to change in the code below so that the background worker thread can change the foreground color in the TextBox?
Answer:
Thanks Andy, it was just that small oversight, here is the corrected code for posterity's sake:
using System.Windows;
using System.ComponentModel;
using System.Threading;
using System.Windows.Media;
namespace TestBackgroundWorker7338
{
public partial class Window1 : Window
{
private BackgroundWorker _worker;
int _percentageFinished = 0;
public Window1()
{
InitializeComponent();
ButtonCancel.IsEnabled = false;
}
private void Button_Start(object sender, RoutedEventArgs e)
{
_worker = new BackgroundWorker();
_worker.WorkerReportsProgress = true;
_worker.WorkerSupportsCancellation = true;
_worker.DoWork += (s, args) =>
{
BackgroundWorker worker = s as BackgroundWorker;
int numberOfTasks = 300;
for (int i = 1; i <= numberOfTasks; i++)
{
if (worker.CancellationPending)
{
args.Cancel = true;
return;
}
Thread.Sleep(10);
float percentageDone = (i / (float)numberOfTasks) * 100f;
worker.ReportProgress((int)percentageDone);
}
};
_worker.ProgressChanged += (s,args) =>
{
_percentageFinished = args.ProgressPercentage;
ProgressBar.Value = _percentageFinished;
Message.Text = _percentageFinished + "% finished";
if (_percentageFinished < 500)
{
Message.Text = "stopped at " + _percentageFinished + "%";
}
else
{
Message.Text = "finished";
}
if (_percentageFinished >= 70)
{
InputBox.Foreground = new SolidColorBrush(Colors.Red);
}
else if (_percentageFinished >= 40)
{
InputBox.Foreground = new SolidColorBrush(Colors.Orange);
}
else if (_percentageFinished >= 10)
{
InputBox.Foreground = new SolidColorBrush(Colors.Brown);
}
else
{
InputBox.Foreground = new SolidColorBrush(Colors.Black);
}
};
_worker.RunWorkerCompleted += (s,args) =>
{
ButtonStart.IsEnabled = true;
ButtonCancel.IsEnabled = false;
ProgressBar.Value = 0;
};
_worker.RunWorkerAsync();
ButtonStart.IsEnabled = false;
ButtonCancel.IsEnabled = true;
}
private void Button_Cancel(object sender, RoutedEventArgs e)
{
_worker.CancelAsync();
}
}
}
As Andy said changing properties of controls must happen on the UI thread.
WPF provides a Dispatcher class that makes it easier to route calls for controls to the appropriate UI thread. Controls in WPF exposes a Dispatcher property object to dispatch the call to an appropriate UI thread.
You can use this as follows (I would add this after the line worker.ReportProgress((int)percentageDone); in the Button_Start method):
// the Window class exposes a Dispatcher property, alternatively you could use
// InputBox.Dispatcher to the same effect
if (!Dispatcher.CheckAccess())
{
Dispatcher.Invoke(new Action(() => ChangeForegroundColor(percentageDone));
}
else
{
ChangeForegroundColor(percentageDone);
}
...
private void ChangeForegroundColor(int percentageDone)
{
if (percentageDone >= 90)
{
InputBox.Foreground = new SolidColorBrush(Colors.Red);
}
else if(percentageDone >=10)
{
InputBox.Foreground = new SolidColorBrush(Colors.Orange);
}
}
The ProgressChanged delegate is run on the UI thread. If you set the BackgroundColor there instead of in DoWork, that should allow you to set the color without the error.