I made a sample Consumer/Producer thread application, so I can learn how to properly use it.
I want it to allow for one thread to send commands to the GUI thread, to update the GUI with content.
I have it working, but there's one small issue. The GUI thread is my consumer thread, so I have it always checking for new commands (using a while loop). The issue is that because of this while loop, the GUI never displays because it's always stuck in the while loop. Note that the string Queue will eventually be replaced with a more complex object (one that holds data & command type).
I'm not sure how else to allow the GUI thread to consume commands without interrupting GUI functionality. Am I doing something wrong?
Here's my Form1.cs code (the form only contains 1 RichTextBox for showing output called OutputBox).
using System;
using System.Collections.Generic;
using System.Threading;
using System.Windows.Forms;
namespace MultiThreading
{
class ThreadCommandQueue
{
public static ThreadCommandQueue instance = new ThreadCommandQueue();
private Queue<string> m_queue;
private Object m_lock;
public static ThreadCommandQueue GetInstance()
{
return instance;
}
private ThreadCommandQueue()
{
m_queue = new Queue<string>();
m_lock = new Object();
}
public void Add(
string data_to_add)
{
lock (m_lock)
{
m_queue.Enqueue(data_to_add);
}
}
public string Get()
{
lock (m_lock)
{
if (m_queue.Count > 0)
{
return m_queue.Dequeue();
}
return null;
}
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void PopulateThreadCommandQueue()
{
int i = 0;
while(true)
{
ThreadCommandQueue.GetInstance().Add("Item #: " + i.ToString());
i++;
}
}
private void Form1_Load(object sender, EventArgs e)
{
// Create the Command Queue....
ThreadCommandQueue.GetInstance();
// Create a Testing Producer Thread
Thread ProducerThread = new Thread(PopulateThreadCommandQueue);
ProducerThread.Start();
// The GUI thread is the Consumer, so keep checking the CommandQueue for data...
while(true)
{
string latest = ThreadCommandQueue.GetInstance().Get();
if(latest != null)
{
OutputBox.Text += latest + "\n";
}
}
}
}
}
Use ConcurrentQueue. So no locking is required to add and get from Queue.
Also you in real time you will not receive commands continuously from UI thread (while loop). If you have such scenario use a separate thread to receive outcome.
Then from the receivers thread you can update UI using Invoke command, as below.
//This method called from receiver thread
public void UpdateForm(Data d)
{
if(this.InvokeRequired)
{
this.Invoke(new MethodInvoker(() => this.UpdateFormUI(r)));
}
else
{
this.UpdateFormUI(data)
}
}
public void UpdateFormUI(Data d)
{
//Does actual ui update
}
Okay, the problem here is that you need to poll the message loop all the time to make GUI working and you also need to poll your IPC command queue all the time to make your commands working and you need this polling to happen at the same time on the same thread.
There are multiple ways to resolve this, but the easiest would be to process the message loop and when there is nothing to process, do your IPC command queue processing. For WinForms application this would be something like:
public Form1()
{
InitializeComponent();
Application.Idle += (sender, eargs) => ProcessCommands();
}
private void ProcessCommands()
{
while(true)
{
string latest = ThreadCommandQueue.GetInstance().Get();
if(string.IsNullOrEmpty(latest)) return;
OutputBox.Text += latest + "\n";
}
}
Related
I have form with button and text box. Button is starting thread which is updating value of text box.
public Form1()
{
InitializeComponent();
myDelegate = new UpdateUi(updateUi);
}
private void button1_Click(object sender, EventArgs e)
{
myThread = new Thread(new ThreadStart(ThreadFunction));
myThread.Start();
}
private void ThreadFunction()
{
MyThreadClass myThreadClassObject = new MyThreadClass(this);
myThreadClassObject.Run();
}
private void updateUi(int i)
{
textBox1.Text = i.ToString();
Thread.Sleep(1000);
}
public Thread myThread;
public delegate void UpdateUi(int i);
public UpdateUi myDelegate;
and ThreadClass:
public class MyThreadClass
{
Form1 myFormControl1;
public MyThreadClass(Form1 myForm)
{
myFormControl1 = myForm;
}
public void Run()
{
// Execute the specified delegate on the thread that owns
// 'myFormControl1' control's underlying window handle.
for(int i=0;i<100;i++)
{
if(myFormControl1.InvokeRequired)
{
myFormControl1.Invoke(myFormControl1.myDelegate,i);
}
}
}
}
As You can see there is nothing special in my code but sometimes the code freeze.
eg it goes 1->2->3->freeze->16->17 and so on.
I took code from HERE with little modifications
The issue is you are delaying the UI thread not the the process itself so what happens is you issue all the update commands but since it all runs on the same thread it gets clogged because the Thread.Sleep stops the UI thread so it runs a bunch of textBox1.Text = i.ToString(); then it stops for all the time of all the Thread.Sleep(1000); probably the number of 1->2->3... you see is equal to the number of cores in your machine.
When you stop the run method what happens is you issue one update command that runs immediately and wait for one second until you issue the next command witch I think its what you are trying to accomplish.
I have a FTP proccess that run without UI. and have a winform that use this ftp control. in that window I have a progressbar that show the ftp upload progress. The progress arrives to the window via interfase that is updated on the underliying presenter (I'm using MVP pattern).
My problem is when try to update the progress, it allways throw me this exception.
Through threads illegal operation: control 'prgProgresoSubido' is accessed from a thread other than that in which you created it.
That problem persists even if I use a BackGroundWorker in the Form.
// This is a delegated on presenter when a File finish to upload
void client_FileUploadCompletedHandler(object sender, FileUploadCompletedEventArgs e)
{
string log = string.Format("{0} Upload from {1} to {2} is completed. Length: {3}. ",
DateTime.Now, e.LocalFile.FullName, e.ServerPath, e.LocalFile.Length);
archivosSubidos += 1;
_Publicacion.ProgresoSubida = (int)((archivosSubidos / archivosXSubir) * 100);
//this.lstLog.Items.Add(log);
//this.lstLog.SelectedIndex = this.lstLog.Items.Count - 1;
}
// This is My interfase
public interface IPublicacion
{
...
int ProgresoSubida { set; }
}
/// And Here is the implementartion of the interfase on the form
public partial class PublicarForm : Form ,IPublicacion
{
//Credenciales para conectarse al servicio FTP
public FTPClientManager client = null;
public XmlDocument conf = new XmlDocument();
public string workingDir = null;
public webTalk wt = new webTalk();
private readonly PublicacionesWebBL _Publicador;
public PublicarForm()
{
InitializeComponent();
String[] laPath = { System.AppDomain.CurrentDomain.BaseDirectory};
String lcPath = System.IO.Path.Combine(laPath);
_Publicador = new PublicacionesWebBL(this, lcPath);
}
public int ProgresoSubida
{
set
{
// This is my prograss bar, here it throw the exception.
prgProgresoSubido.Value = value;
}
}
}
How can I do to avoid this problem ?
In general, all updates to the User Interface and Controls has to be done from the main thread (event dispatcher). If you attempt to modify the properties of a control from a different thread you will get an exception.
You must call Control.Invoke to invoke on the event dispatcher the method that updates your UI
Control.Invoke
Here, place a button and a label on a form, then try this
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(TestThread));
t.Start();
}
private void TestThread()
{
for (int i = 0; i < 10; i++)
{
UpdateCounter(i);
Thread.Sleep(1000);
}
}
private void UpdateCounter(int i)
{
if (label1.InvokeRequired)
{
label1.Invoke(new ThreadStart(delegate { UpdateCounter(i); }));
}
else
{
label1.Text = i.ToString();
}
}
}
Realize, that if you are firing an event from a thread, that the event will be on the same Thread. Therefore, if that thread is not the event dispatcher, you'll need to invoke.
Also, there may be mechanisms that BackgroundWorker gives you (As the commentator said) that simplify this for you, but I've never used it before so I'll leave that up to you to investigate.
As Alan has just pointed out, you must do all operations with UI controls in UI thread.
Just modify your property like this:
public int ProgresoSubida
{
set
{
MethodInvoker invoker = delegate
{
prgProgresoSubido.Value = value;
}
if (this.InvokeRequired)
{
Invoke(invoker);
}
else
{
invoker();
}
}
}
I have a service running some different tasks in a loop until the service is stopped.
However one of these tasks i calling a web service and this call can take several minutes to complete. I want to be able to stop the service instantly, 'cancelling' the web service call without calling Thread.Abort because that causes some strange behavior even if the only thing the thread is doing is calling this web service method.
How can i cancel or break from a synchronous method call (if it's even possible)?
Or should I try a different approach?
I have tried to use the AutoResetEvent and then calling Thread.Abort which is working fine in the below code sample, but when implementing this solution in the actual service I get some unexpected behavior probably because of what's going on in the external libraries I'm using.
AutoResetEvent and Thread.Abort:
class Program
{
static void Main(string[] args)
{
MainProgram p = new MainProgram();
p.Start();
var key = Console.ReadKey();
if (key.Key == ConsoleKey.Q)
p.Stop();
}
}
class MainProgram
{
private Thread workerThread;
private Thread webServiceCallerThread;
private volatile bool doWork;
public void Start()
{
workerThread = new Thread(() => DoWork());
doWork = true;
workerThread.Start();
}
public void Stop()
{
doWork = false;
webServiceCallerThread.Abort();
}
private void DoWork()
{
try
{
while (doWork)
{
AutoResetEvent are = new AutoResetEvent(false);
WebServiceCaller caller = new WebServiceCaller(are);
webServiceCallerThread = new Thread(() => caller.TimeConsumingMethod());
webServiceCallerThread.Start();
// Wait for the WebServiceCaller.TimeConsumingMethod to finish
WaitHandle.WaitAll(new[] { are });
// If doWork has been signalled to stop
if (!doWork)
break;
// All good - continue
Console.WriteLine(caller.Result);
}
}
catch (Exception e)
{
Console.Write(e);
}
}
}
class WebServiceCaller
{
private AutoResetEvent ev;
private int result;
public int Result
{
get { return result; }
}
public WebServiceCaller(AutoResetEvent ev)
{
this.ev = ev;
}
public void TimeConsumingMethod()
{
try
{
// Simulates a method running for 1 minute
Thread.Sleep(60000);
result = 1;
ev.Set();
}
catch (ThreadAbortException e)
{
ev.Set();
result = -1;
Console.WriteLine(e);
}
}
}
Can someone suggest a solution to this issue?
Try this
public void Start()
{
workerThread = new Thread(() => DoWork());
doWork = true;
workerThread.IsBackground = true;
workerThread.Start();
}
A thread is either a background thread or a foreground thread.
Background threads are identical to foreground threads, except that
background threads do not prevent a process from terminating. Once all
foreground threads belonging to a process have terminated, the common
language runtime ends the process. Any remaining background threads
are stopped and do not complete.
For more details see http://msdn.microsoft.com/en-us/library/system.threading.thread.isbackground.aspx
The solution is really this simple: Don't make calls that block for several minutes unless you want to block for several minutes. If there is no way to do a particular thing without blocking, potentially for several minutes, complain loudly to whoever wrote the code that imposes that painful requirement (or fix it yourself, if possible).
Once you've made the call, it's too late. You're committed. If the function you are calling doesn't provide a safe way to abort it, then there's no safe way.
As all you want to do is make one an asynchonrous web service call at a time and on each response make another call you can dispense with the worker thread and simply make an aynchronous call, register a callback and make another async call from the callback:
class Program
{
private static WebServiceCaller.TCMDelegate _wscDelegate;
private static readonly WebServiceCaller _wsCaller = new WebServiceCaller();
static void Main(string[] args)
{
_wscDelegate = _wsCaller.TimeConsumingMethod;
MakeWSCallAsync();
Console.WriteLine("Enter Q to quit");
while (Console.ReadLine().ToUpper().Trim()!="Q"){}
}
public static void MakeWSCallAsync()
{
_wscDelegate.BeginInvoke(OnWSCallComplete, null);
}
public static void OnWSCallComplete(IAsyncResult ar)
{
Console.WriteLine("Result {0}", _wscDelegate.EndInvoke(ar));
MakeWSCallAsync();
}
}
class WebServiceCaller
{
public delegate int TCMDelegate();
public int TimeConsumingMethod()
{
try
{
// Simulates a method running for 1 minute
Thread.Sleep(1000);
return 1;
}
catch (ThreadAbortException e)
{
return -1;
}
}
}
No blocking (well, the console thread is blocking on ReadLine()) and no windows kernal mode sync objects (AutoResetEvent) which are expensive.
I have been trying to do due-diligence before asking this question, but I can't seem to find what I'm looking for. I believe that I'm running up against producer-consumer problem. I'm writing a winforms application in C# that uses two threads: one for the UI, and one for a background worker. Items are added to a task queue via the submit button event handler (ie. whenever the user hits "Submit"). If there is nothing in the queue already, then the background worker is called and starts to process the queue. If the background worker is busy, then the task is simply added to the queue. Theoretically, the background worker will move on to the next item in the queue when it finishes its current work.
In my UI thread, I have the following code that instantiates a DiscQueue object and then adds items to its queue:
private DiscQueue discQueue = new DiscQueue();
this.discQueue.AddToQueue(currentCD);
Below is my DiscQueue class. My AddToQueue function adds the disc to the queue and then calls RunWorkerAsync() if the bw is not already busy. Then, in bw_DoWork, I grab an item from the queue and do the work on it that I need to do. When the bw completes its task, it should call bw_RunWorkerCompleted, which should direct it to continue working through the queue if there are more items in the queue.
class DiscQueue
{
private Queue<Disc> myDiscQueue = new Queue<Disc>();
private BackgroundWorker bw = new BackgroundWorker();
// Initializer
public DiscQueue()
{
// Get the background worker setup.
this.bw.WorkerReportsProgress = false;
this.bw.WorkerSupportsCancellation = false;
this.bw.DoWork += new DoWorkEventHandler(bw_DoWork);
}
public void AddToQueue(Disc newDisc)
{
this.myDiscQueue.Enqueue(newDisc);
if (!this.bw.IsBusy)
{
this.bw.RunWorkerAsync();
}
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
DiscPreparationFactory discToPrepare = new DiscPreparationFactory();
Disc currentDisc = new Disc();
currentDisc = this.myDiscQueue.Dequeue();
discToPrepare.PrepareAndPublish(currentDisc);
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (this.myDiscQueue.Count > 0)
{
this.bw.RunWorkerAsync();
}
}
}
In testing, I discovered that adding items to the queue in rapid succession somehow clobbers the queue so that all items in the queue are assigned the value of (perhaps the reference to?) the last item added to the queue. As I stepped through the code in debugging, this seems to happen by the time you get to if (!this.bw.IsBusy) in the AddToQueue function. I'm not sure what's going on.
Aside from answering my specific question, I'm sure that I'm doing things bass-ackwards, and I'd be very happy to know the "right way" to do this.
EDIT: Here is my Disc class:
public class Disc
{
public enum DiscFormat
{
Audio,
Data,
}
private string sku;
private int quantity;
private DiscFormat format;
public string Sku
{
get
{
return this.sku;
}
set
{
this.sku = value;
}
}
public DiscFormat Format
{
get
{
return this.format;
}
set
{
this.format = value;
}
}
public int Quantity
{
get
{
return this.quantity;
}
set
{
this.quantity = value;
}
}
}
EDIT 2: My DiscQueue object is instantiated in my MainForm.cs file as follows:
public partial class MainForm : Form
{
Disc currentCD = new Disc();
private DiscQueue discQueue = new DiscQueue();
public MainForm()
{
// Do some stuff...
}
// Skipping over a bunch of other methods...
private void buttonSubmit_Click(object sender, EventArgs e)
{
currentCD.Sku = this.textBoxSku.Text;
currentCD.Quantity = (int)this.numericUpDownQuantity.Value;
if (this.radioButtonAudio.Checked)
currentCD.Format = Disc.DiscFormat.Audio;
else
currentCD.Format = Disc.DiscFormat.Data;
this.discQueue.AddToQueue(currentCD);
}
}
The standard .Net Queue<T> is not "thread safe"; emphasis mine:
A Queue<T> can support multiple readers concurrently, as long as the collection is not modified. Even so, enumerating through a collection is intrinsically not a thread-safe procedure. To guarantee thread safety during enumeration, you can lock the collection during the entire enumeration. To allow the collection to be accessed by multiple threads for reading and writing, you must implement your own synchronization.
If you have .Net 4.0 you should look into using a ConcurrentQueue<T> instead.
If not, you can protect read/write access inside your DiscQueue with a simple lock:
/// <summary>
/// Synchronizes access to <see cref="DiscQueue.myDiscQueue" />.
/// </summary>
private object queueLock = new object();
Then your reader would use the lock like:
Disc currentDisc = null;
lock (this.queueLock)
{
// protect instance members of Queue<T>
if (this.myDiscQueue.Count > 0)
{
currentDisc = this.myDiscQueue.Dequeue();
}
}
// work with currentDisk
And your writer would use the lock like:
lock (this.queueLock)
{
this.myDiscQueue.Add(currentCD);
}
i have a STA-Thread which adds a PrintJob to a PrinterQueue. This PrintJob should be monitored and i figuered a Timer would be the right thing to do. But obviously i cannot access the PrintSystemJobInfo-Object in the timer-thread.
How can i solve this situation?
I got the idea that i have to use a synchronized object but i don't know how to and i'm not sure if this will solve my problem... Or is there another technique (dispatcher maybe?) i can make use of?
Is it possible to make the timer STA so i can create the printjob in the timer thread (sta is required for printqueue.addjob)? But i guess this would be a workaround...
Any help much appreciated...
thx, effdee
edit:
some pseudocode:
class WcfService
{
public int ServiceFunc() {
Thread staThread = new Thread(myObj.myFunc);
staThread.setSTA() and start()
staThread.join()
return myObj.status;
}
}
-
class myObj
{
public int status;
public PrintSystemJobInfo printJob;
public Dispatcher d;
public void myFunc()
{
d = CurrentDispatcher;
printJob = printQueue.AddJob(...);
if (printJob == null) status = 0;
else status = 1;
Timers.Timer timer = new Timer(invokeTimer);
timer.Start();
}
public void invokeTimer(args)
{
d.Invoke(new Action(() => { timerFunc(args) }));
}
public void timerFunc(args)
{
//access printJob problem here ;)
writePrintJobDetailsToDatabase();
}
}
You could expose the PrintSystemJobInfo Object to both threads and lock it when you use it. This should suspend the use of the object in a thread if the other one is using it.
wait, nevermind. You need to make a method, addJob, in your mainthread (with STA) which calls AddJob(). In the other thread you can invoke that method with Dispatcher.Invoke and run the method on the main thread.
Or just do this:
void Thread2()
{
Dispatcher.Invoke(new Action(() =>
{
PrintSystemJobInfo.AddJob();
//or any other methods you want executed on the main thread
}
}