I've got a Stream object and I am using BeginRead to begin reading (obviously) into a buffer; the AsyncCallback function is called once the reading is complete. Within this function I can check if the user wants to get the next 'block' and start the BeginRead process again.
The problem I have is the user may choose to cancel while the stream is still reading (so before the AsyncCallback function is called), so how can I cancel the reading of the stream?
Just to further explain the issue - it seems I would have the same outcome if I use a BackgroundWorker with the Streams Read method or the asynchronous BeginRead method. The user could be left waiting for any length of time for the Read/BeginRead method to complete before I can check if the stream should stop reading.
Edit: The code below should do the job, I'm a million miles away from being anything decent in C# so it may well have a couple of bugs as I doubt it's perfect, although it does demonstrate the solution.
In brief, the CWorkManager manages a certain number of threads (which are held within a CWorkerDetail class). Each CWorkerDetail has a status, which can be EWaiting meaning the worker can be started, EReading which means the worker is reading from a source, during which time the worker can be stopped instantly, EWriting which saves the data that was read to the disk - this cannot be stoppped instantly and this process must complete before the thread is stopped. Finally there is EAborting which is set by the manager if the worker should be aborted as soon as possible; right now this is only set if the worker is in the middle of something which cannot be interrupted (such as writing to disk).
Right now, there isn't actually any reading or writing going on, as that would just complicate the main solution (which is basically just the StopWorker function checking a flag of CWorker to see if it can abort instantly); as such we simply cause the thread to sleep.
The GUI side is fairly simple with just a listbox (which shows the status of each worker) and a stop and start button. All code is below, hope this helps somebody, but as I say I'm not brilliant with C# so please watch out for bugs etc...
CWorkManager.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace ThreadApplication {
//A worker that spawns a number of threads (managed internally) that does nothing useful at all.
public class CWorkManager {
//The status of the worker.
public enum EWorkerStatus {
EWaiting,
EReading,
EWriting,
EAborting,
}
//Holds all data relevant to the worker.
private class CWorkerDetails {
//Simple variables.
private readonly Object _Lock=new Object();
private Thread gThread;
private EWorkerStatus gStatus;
private CWorkManager gParentInstance;
private int gIndex;
//Simple constructor.
public CWorkerDetails(int aIndex, CWorkManager aParentInstance, Thread aThread, EWorkerStatus aStatus) {
gIndex=aIndex;
gParentInstance=aParentInstance;
gThread=aThread;
gStatus=aStatus;
}
//Simple get set methods.
public Thread GetThread() { lock(_Lock) { return gThread; } }
public EWorkerStatus GetStatus() { lock(_Lock) { return gStatus; } }
//Sets the status and automatically updates the GUI.
public void SetStatus(EWorkerStatus aStatus) {
lock(_Lock) {
gStatus=aStatus;
Form1.gInstance.Invoke(new UpdateGUIDelegate(gParentInstance.UpdateGUI), new object[] { gIndex, GetStatus() });
}
}
}
//Worker variable.
private List<CWorkerDetails> gWorkers;
//Simple constructor.
public CWorkManager(int aWorkerCount){
gWorkers=new List<CWorkerDetails>();
for(int tIndex=0; tIndex<aWorkerCount; tIndex++)
gWorkers.Add(null);
}
//Creates and starts the worker.
public void StartWorker(int aWorkerIndex) {
//Create a new worker if there is none or if it is waiting to start.
if(gWorkers.ElementAt(aWorkerIndex)==null||gWorkers.ElementAt(aWorkerIndex).GetStatus()==EWorkerStatus.EWaiting)
gWorkers[aWorkerIndex]=new CWorkerDetails(aWorkerIndex, this, new Thread(new ParameterizedThreadStart(WorkerMethod)), EWorkerStatus.EWaiting);
//If the worker is waiting to start, then start.
if(gWorkers.ElementAt(aWorkerIndex).GetStatus()==EWorkerStatus.EWaiting)
gWorkers.ElementAt(aWorkerIndex).GetThread().Start(gWorkers.ElementAt(aWorkerIndex));
}
//Stops the worker.
public void StopWorker(int aWorkerIndex) {
//Do nothing if the worker is null.
if(gWorkers.ElementAt(aWorkerIndex)==null)
return;
//Do nothing if the worker is waiting.
if(gWorkers.ElementAt(aWorkerIndex).GetStatus()==EWorkerStatus.EWaiting)
return;
//If the worker is reading we can abort instantly.
if(gWorkers[aWorkerIndex].GetStatus()==EWorkerStatus.EReading) {
gWorkers[aWorkerIndex].GetThread().Abort();
gWorkers[aWorkerIndex].SetStatus(EWorkerStatus.EWaiting);
return;
}
//Since the worker is not reading or waiting, we have to request the
//worker to abort by itself.
gWorkers[aWorkerIndex].SetStatus(EWorkerStatus.EAborting);
}
//Updates the GUI.
private delegate void UpdateGUIDelegate(int aIndex, EWorkerStatus aStatus);
private void UpdateGUI(int aIndex, EWorkerStatus aStatus) {
Form1.gInstance.SetThreadStatus(aIndex, aStatus);
}
//This method is where all the real work happens.
private void WorkerMethod(Object aWorker) {
//Fetch worker.
CWorkerDetails mWorker=(CWorkerDetails)aWorker;
//Loop forever, the thread will exit itself when required.
while(true) {
//Is the worker status aborting - if so we stop here.
if(mWorker.GetStatus()==EWorkerStatus.EAborting) {
mWorker.SetStatus(EWorkerStatus.EWaiting);
return;
}
//This would normally be reading from a stream which would cause the thread
//to block, simulate this by just sleeping the thread.
mWorker.SetStatus(EWorkerStatus.EReading);
Thread.Sleep(3000);
//Is the worker status aborting - if so we stop here.
if(mWorker.GetStatus()==EWorkerStatus.EAborting) {
mWorker.SetStatus(EWorkerStatus.EWaiting);
return;
}
//All data has been read, set status to writing and again simulate by
//sleeping the thread.
mWorker.SetStatus(EWorkerStatus.EWriting);
Thread.Sleep(3000);
}
}
}
}
Form1.cs:
Contains:
A List box (ListBox_WorkerStatus)
A button (Button_Start)
A button (Button_Stop)
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;
namespace ThreadApplication {
public partial class Form1:Form {
public static Form1 gInstance;
private CWorkManager gManager;
public Form1() {
InitializeComponent();
Button_Start.Click+=new EventHandler(Button_Start_Click);
Button_Stop.Click+=new EventHandler(Button_Stop_Click);
gInstance=this;
for(int tIndex=0; tIndex<5; tIndex++)
ListBox_WorkerStatus.Items.Add("Created");
gManager=new CWorkManager(ListBox_WorkerStatus.Items.Count);
}
public void SetThreadStatus(int aIndex, CWorkManager.EWorkerStatus aStatus) {
ListBox_WorkerStatus.Items[aIndex]=aStatus.ToString();
}
private void Button_Start_Click(object sender, EventArgs e) {
if(ListBox_WorkerStatus.SelectedIndex>=0) {
gManager.StartWorker(ListBox_WorkerStatus.SelectedIndex);
}
}
private void Button_Stop_Click(object sender, EventArgs e) {
if(ListBox_WorkerStatus.SelectedIndex>=0) {
gManager.StopWorker(ListBox_WorkerStatus.SelectedIndex);
}
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e) {
for(int tIndex=0; tIndex<ListBox_WorkerStatus.Items.Count; tIndex++) {
gManager.StopWorker(tIndex);
}
}
}
}
Please Take look atCancel BeginRead this
Use BackgroundWorker
BackgroundWorker backgroundWorker1= new backgroundWorker()
private void InitializeBackgroundWorker()
{
backgroundWorker1.DoWork +=
new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.WorkerSupportsCancellation = true;
}
private void backgroundWorker1_DoWork(object sender,
DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
e.Result = YourWorkToDo();
}
public void Start()
{
backgroundWorker1.RunWorkerAsync()
}
public voic Cancel()
{
backgroundWorker1.CancelAsync();
{
If you want more help leave comment
Related
When I call BuildCustomer.StartTask, I then call a method WriteToDatabase. Inside WriteToDatabase, I want to send a status back to the MainForm to write the status to the GUI. When the code reaches that point, my application freezes up and gives no error. I did find out that if I remove task.Wait(), it stops freezing and works. But I think I want the wait in because my BuildCustomer takes a bit of time and writes a lot of updates (including more updates from Common class) to the GUI. Can someone tell me what is wrong or what I should be doing differently? This is a .Net 4 project so I cannot use async, which I've seen other answers for.
public partial class MainForm : Window
{
public MainForm()
{
Common.SendMessage += UpdateStatus;
}
private void Button_Click(object sender, EventArgs e)
{
BuildCustomer.StartTask();
}
private void UpdateStatus(string message)
{
Dispatcher.Invoke(new Action(() =>
{
StatusTextBox.Text = message;
}));
}
}
public class BuildCustomer
{
public static void StartTask()
{
var action = new Action<object>(BuildCustomer);
var task = new Task(() => action(buildDetails));
task.Start();
task.Wait();
}
private void BuildCustomerDetails(object buildDetails)
{
Common.WriteToDatabase();
}
}
public class Common
{
public delegate void MessageLogDelegate(string message);
public static event MessageLogDelegate SendMessage;
public static void WriteToDatabase()
{
SendMessage("Some status message to write back to the GUI");
}
}
You have a deadlock. The StartTask waits on task.Wait() to complete but this occurs (is called on) on the calling thread which is the main UI thread.
The Task being waited eventually reaches UpdateStatus which calls an Invoke on the UI thread as well but this thread is currently waiting on task.Wait() (so it is blocking which results in the UI thread not being available indefinitely).
Try to add async keyword to method signature and use this:
await task;
It cause to does not sleep main thread(UI thread).
I have created a simple form home and there is another file Mouse_Tracking.cs.
Mouse_Tracking.cs class is a thread class. I want to start and stop that thread using two different button click in home form.
How can I do this ?
Main form:
namespace computers
{
public partial class home : Form
{
public home()
{
InitializeComponent();
}
private void btn_start_Click(object sender, EventArgs e)
{
var mst = new Mouse_Tracking();
Thread thread1 = new Thread(new ThreadStart(mst.run));
thread1.Start();
}
private void btn_stop_Click(object sender, EventArgs e)
{
//Here I want to stop "thread1"
}
}
}
Computers class:
namespace computers
{
public class Mouse_Tracking
{
public void run()
{
// Some method goes here
}
}
You shouldn't kill threads from the outside. Instead, you should gently ask your thread to terminate, and in your thread you should respond to that request and return from the thread procedure.
You could use an event for that. E.g. add the following to your form class:
AutoResetEvent evtThreadShouldStop = new AutoResetEvent();
In your run method, check if the svtThreadShouldStop event is set every 0.1-1 seconds, if it’s set, return from the thread function, e.g. if( evtThreadShouldStop.WaitOne( 0 ) ) return;
And in your btn_stop_Click call evtThreadShouldStop.Set();
P.S. It’s rarely a good decision to create your own thread: creating and destroying threads is expensive. The runtime already has the thread pool you can use for your own background processing. To post your background task to a pool thread instead use e.g. ThreadPool.QueueUserWorkItem method. You can use same technique with AutoResetEvent to request task termination.
P.P.S. The name of the Mouse_Tracking class suggest you're trying to interact with mouse from the background thread? You can't do that: you can only interact with the GUI including mouse and keyboard from the GUI thread.
Here is an example of what Soonts has suggested. It's quite old-style solution but it's simple and will work fine. But there is a number of other approaches. You can use BackgroundWorker or TPL (Task class), each of which have own thread stop mechanisms.
And I believe that it's ok to create own thread without using existing thread pool if you don't need to do it too often.
public class Mouse_Tracking
{
private ManualResetEvent _stopEvent = new ManualResetEvent(false);
public void stop()
{
_stopEvent.Set();
}
public void run()
{
while (true)
{
if (_stopEvent.WaitOne(0))
{
//Console.WriteLine("stop");
// handle stop
return;
}
//Console.WriteLine("action!");
// some actions
Thread.Sleep(1000);
}
}
}
Sometimes its quite difficult to maintain the thread. You can achieve it by using BackgroundWorker class. You will get complete demonstration on how to use it is here Stop Watch Using Background Worker. I hope it will be useful.
You could use a class like this for controlling your thread(s):
class ThreadController {
private Thread _thread;
public void Start(ThreadStart start) {
if (_thread == null || !_thread.IsAlive) {
_thread = new Thread(start);
_thread.Start();
}
}
public void Stop() {
if (_thread != null && _thread.IsAlive) {
_thread.Interrupt(); // Use _thread.Abort() instead, if your thread does not wait for events.
_thread = null;
}
}
}
Then use:
public partial class home : Form
{
public home()
{
InitializeComponent();
_thread = new ThreadController();
}
private readonly ThreadController _thread;
private void btn_start_Click(object sender, EventArgs e)
{
var mst = new Mouse_Tracking();
_thread.Start(mst.run);
}
private void btn_stop_Click(object sender, EventArgs e)
{
_thread.Stop();
}
}
I'm creating a weather app that polls temperature from a service I've made:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace Weather
{
class Program
{
static BackgroundWorker bgw;
static void Main(string[] args)
{
bgw = new BackgroundWorker();
bgw.DoWork += bgw_DoWork;
bgw.RunWorkerAsync();
}
static async void bgw_DoWork(object sender, DoWorkEventArgs e)
{
Weather bot = new Weather();
if (bot.IsRunning)
{
await bot.Update();
}
}
}
public class Weather
{
public bool IsRunning { get; set; }
private DateTime lastUpdated;
public Weather()
{
IsRunning = true;
lastUpdated = DateTime.Now.AddDays(-1);
}
public async Task<bool> Update()
{
if (lastUpdated < DateTime.Now)
{
lastUpdated = DateTime.Now.AddSeconds(30);
// temperature
double value = await GetLatestValue("New York");
}
return true;
}
private async Task<double> GetLatestValue(string city)
{
string url = "http://www" + city;
var client = new WebClient();
string data = await client.DownloadStringTaskAsync(url);
return 4.3;
}
}
}
The problem here is that it does not seem to work? The GetLatesValue function is just jibberish, will just return 4.3 for testing purposes.
What happens is that on await GetLatestValue the console application just quits.
The problem is simpler than you might think: you are running a BackgroundWorker, which basically wraps a thread that has .IsBackground = true. Such threads will not keep a process alive - they will be shut down automatically when the process exits. The process will exit when all non-background threads are completed.
Your Main method starts the BackgroundWorker, but then does nothing else - Main exits, and the application is complete. The BackgroundWorker is then shut down at whatever point it's reached. There's nothing wrong with the code it's running - but the app is shutting down without letting it complete.
EDIT: if you want to test this, simply put a Console.ReadLine() at the end of your Main - it'll keep the application alive until you press Enter, and so you should see your thread run until you do.
In addition to Dan Puzey's answer, there's not much sense in assigning an async void method as an event handler for BackgroundWorker, in the first place.
Your worker method bgw_DoWork will return and the background thread will be finished as soon as the execution point hits the first await inside bgw_DoWork. The bot.Update task most likely still will be pending at that point.
You don't need a BackgroundWorker here. The code can be as simple as this:
static void Main(string[] args)
{
DoWorkAsync().Wait();
}
static async Task DoWorkAsync()
{
Weather bot = new Weather();
if (bot.IsRunning)
{
await bot.Update();
}
}
This is what I want to do:
Have a timer with some interval
In the timer callback code, if some condition is met, another thread should be run
I’ve put my code in a class which is instantiated by the main form and the code is executed upon method call (‘StartSync()’, se sample code).
The problem is that the code runs for a couple of seconds but then terminates. I suppose I’m doing something stupid but I really can’t see what it is. Thankful for any help with regards to this.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;
namespace WindowsFormsApplication1
{
class Syncer
{
static bool SYNC_IN_PROGRESS;
public void StartSync()
{
SYNC_IN_PROGRESS = false;
Timer timer = new Timer(timerCallback, null, 0, 1000);
}
public void timerCallback(Object stateInfo)
{
Debug.WriteLine("Sync?");
if (!SYNC_IN_PROGRESS)
{
SYNC_IN_PROGRESS = true;
Thread thSync = new Thread(new ThreadStart(sync));
thSync.Start();
}
}
void sync()
{
Debug.WriteLine("Syncing...");
SYNC_IN_PROGRESS = false;
}
}
}
At a guess, the Timer is only held in a method variable; it sounds to me like the Timer is getting garbage collected and finalized, hence terminated. I suspect you should hold onto that reference in a field to prevent collection.
As an aside - I doubt it is the cause here, but when dealing with threading you should be religiously aware of access to shared state from multiple threads; for example:
using Monitor (aka lock)
appropriate use of volatile
Interlocked when it fits
Your current access to the static bool will probably work OK, but...
Try this cleaner approach
static volatile bool SYNC_IN_PROGRESS;
static thread syncPoll;
public void StartSync()
{
SYNC_IN_PROGRESS = false;
syncPoll = new Thread(sync);
syncPoll.Start();
}
void sync()
{
while (true)
{
Debug.WriteLine("Sync?");
if (SYNC_IN_PROGRESS) Debug.WriteLine("Syncing...");
Thread.Sleep(1000);
}
}
It does the same you try to do with your current code :) but doesn't use a timer
So here is what I did and it seems to work just fine
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
StartSync();
}
static bool SYNC_IN_PROGRESS;
public void StartSync()
{
SYNC_IN_PROGRESS = false;
System.Threading.Timer timer = new System.Threading.Timer(timerCallback, SYNC_IN_PROGRESS, 0, 1000);
}
public void timerCallback(Object stateInfo)
{
Debug.WriteLine("Sync?");
if (!(bool)stateInfo)
{
SYNC_IN_PROGRESS = true;
Thread thSync = new Thread(new ThreadStart(sync));
thSync.Start();
}
}
void sync()
{
Debug.WriteLine("Syncing...");
SYNC_IN_PROGRESS = false;
}
}
I tried to wrap the dispatcher in a thread. But the result is not what i expect. How can i solve that problem?
public void Start()
{
ThreadStart ts = inner;
Thread wrapper = new Thread(ts);
wrapper.Start();
}
private void inner()
{
_Runner.Dispatcher.Invoke(_Runner.Action, DispatcherPriority.Normal);
}
You have not shown us enough code/explained yourself well enough to be able to provide a good answer, but I'm guessing your action (_Runner.Action) is expensive and slow to execute. If so, that is why your UI is unresponsive. You're essentially telling the Dispatcher to run that expensive operation on the UI thread when what you really want to do is run as much of your operation on the background thread as possible, and then marshal back to the UI thread via the Dispatcher only when necessary.
When you fire an action through/on the dispatcher, that action is called on the UI thread.
My guess is that you are doing the work/processing in the _Runner.Action function and it is tying up the UI thread. You'll have to do the main processing part in the inner() function and then call the Dispatcher for the final update details.
If you absolutely must process on the dispatcher, break your process into smaller pieces and call Dispatcher.BeginInvoke() for each piece so other events can be processed in between your process.
You need to break Runner.Action into two parts - the long running part that does the calculation and the part that updates the GUI.
After you do that you call the long running part in the background thread and use the dispatcher only on the UI update part.
By the way, you should also probably use BeginInvoke and not Invoke.
If the long running part of Runner.Action is updating the GUI than you can't use a background thread to solve your problem - there are solutions for slow GUI operations but they change depending on what exactly you are trying to do.
Here is an example that will let you run WPF applications with multiple UI threads. I believe this will help you. Refer to this http://eprystupa.wordpress.com/2008/07/28/running-wpf-application-with-multiple-ui-threads/
Thread lThread = new Thread(() =>
{
var lWnd = new Window1();
lWnd.Show();
lWnd.Closed += (sender2, e2) => lWnd.Dispatcher.InvokeShutdown();
System.Windows.Threading.Dispatcher.Run();
});
lThread.SetApartmentState(ApartmentState.STA);
lThread.Start();
Ditto what everyone here has said.
Additionally, you may want to look into using the BackgroundWorker class.
This is what I have started using for background tasks... I have not been using it long, so I don't know if there are bugs.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SSA.Utility
{
public class BackgroundTaskManager : IDisposable
{
private System.Windows.Threading.Dispatcher _OwnerDispatcher;
private System.Windows.Threading.Dispatcher _WorkerDispatcher;
private System.Threading.Thread _WorkerThread;
private Boolean _WorkerBusy;
private System.Threading.EventWaitHandle _WorkerStarted = new System.Threading.EventWaitHandle(false, System.Threading.EventResetMode.ManualReset);
public BackgroundTaskManager()
{
_OwnerDispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher;
_WorkerThread = new System.Threading.Thread(new System.Threading.ThreadStart(WorkerStart));
_WorkerThread.Name = "BackgroundTaskManager:" + DateTime.Now.Ticks.ToString();
_WorkerThread.IsBackground = true;
_WorkerThread.Start();
_WorkerStarted.WaitOne();
}
public Boolean IsBusy
{
get { return _WorkerBusy; }
}
public System.Windows.Threading.Dispatcher Dispatcher
{
get {
return _WorkerDispatcher;
}
}
public System.Windows.Threading.Dispatcher OwnerDispatcher
{
get
{
return _OwnerDispatcher;
}
}
private void WorkerStart()
{
_WorkerDispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher;
_WorkerDispatcher.Hooks.DispatcherInactive += WorkDone;
_WorkerDispatcher.Hooks.OperationPosted += WorkAdded;
_WorkerStarted.Set();
System.Windows.Threading.Dispatcher.Run();
}
private void WorkAdded(Object sender, System.Windows.Threading.DispatcherHookEventArgs e)
{
_WorkerBusy = true;
}
private void WorkDone(Object sender, EventArgs e)
{
_WorkerBusy = false;
}
public void Dispose()
{
if (_WorkerDispatcher != null)
{
_WorkerDispatcher.InvokeShutdown();
_WorkerDispatcher = null;
}
}
}
}
// Useage (not tested)
private SSA.Utility.BackgroundTaskManager _background = new SSA.Utility.BackgroundTaskManager();
public void LongTaskAsync()
{
_background.Dispatcher.BeginInvoke(new Action(LongTask), null);
}
public void LongTask()
{
System.Threading.Thread.Sleep(10000); // simulate a long task
_background.OwnerDispatcher.BeginInvoke(new Action<STATUSCLASS>(LongTaskUpdate), statusobject);
}
public void LongTaskUpdate(STATUSCLASS statusobject) {
}
Yes. _Runner.Action is the problem. Some long-timed methods used in the Dispatcher block. But solution is "dont use the any thread not related to UI in the dispatcher"