This question already has an answer here:
Run work on specific thread
(1 answer)
Closed 1 year ago.
To understand my problem more precisely I would like to use a WPF application with two buttons as an example.
public RelayCommand Button1Command { get; private set; }
public RelayCommand Button2Command { get; private set; }
In my ctor:
Button1Command = new RelayCommand(Button1Method);
Button2Command = new RelayCommand(Button2Method);
public void Button1Method(object sender)
{
//Should do some stuff but e.g. in ThreadId 2
}
public void Button2Method(object sender)
{
//Should do some stuff but if Button1 was executed in ThreadId 2
//this button Action should also be done in ThreadId 2
}
The following example:
User clicks on Button1 => Button1Method is started on a new thread with ID 2.
User clicks on Button2 => Button2Method is started at the earliest when Button1Method is finished but also on the same thread in which Button1Method was executed.
And the whole also in the other direction.
The way I would do this, is by having a worker thread to which you can add Tasks to complete sequentially. You can press the buttons in any order, but it will always wait for the previous one task to complete first before continuing work.
public class MyClass
{
private Queue<Func<Task>> _tasks;
private Thread _workerThread;
private bool _running;
// Constructor
public MyClass()
{
// Initialize Variables
_tasks = new Queue<Func<Task>>();
_workerThread = new Thread(DoWork);
// Start the thread.
_workerThread.Start();
}
// Deconstructor
~MyClass() => _running = false;
// Worker thread Function
private void DoWork(object obj)
{
_running = true;
while (_running)
{
if (_tasks.Count() > 0)
{
var task = _tasks.Dequeue();
var taskResult = task.Invoke();
taskResult.Wait();
}
}
}
// Your buttons
public void ButtonAPressed() => _tasks.Enqueue(ActionA);
public void ButtonBPressed() => _tasks.Enqueue(ActionB);
// Tasks that you need to queue up
private async Task ActionA() { /* Do Work */ }
private async Task ActionB() { /* Do Work */ }
}
Related
I have application with 1 simple thread:
public partial class MainWindow : Window
{
Thread historyThread = null;
Window historyWindow = null;
public MainWindow()
{
//...
}
#region EVENTS Magazyn
private void btn_K_FinalizujZakup_Click(object sender, RoutedEventArgs e)
{
if (dg_Klient.Items.Count > 0)
{
//Problem with 'Finalizuj' Method
new Historia.Wpis(MainWindowViewModel.klient).Finalizuj(viewModel.historiaWindow_ViewModel.historiaZakupow);
MainWindowViewModel.klient = new Klient.Klient();
dg_Klient.ItemsSource = MainWindowViewModel.klient;
}
}
#region History Thread
private void btn_K_HistoriaOpen_Click(object sender, RoutedEventArgs e)
{
//if(historyThread != null){
// historyThread.Abort();
// historyThread = null;
//}
historyThread = new Thread(new ThreadStart(History_ThreadStart));
historyThread.SetApartmentState(ApartmentState.STA);
historyThread.IsBackground = true;
historyThread.Start();
}
private void History_ThreadStart()
{
historyWindow = new HistoryWindow(viewModel.historiaWindow_ViewModel);
historyWindow.Show();
historyWindow.Activate();
System.Windows.Threading.Dispatcher.Run();
}
#endregion // History Thread
//...
}
and 'Wpis' Class look like:
public class Wpis
{
private DateTime date;
public DateTime Date // normal get/ set
private ObservableCollection<Produkt> listaZakupow;
public ObservableCollection<Produkt> ListaZakupow // normal get/set
public Wpis(ObservableCollection<Produkt> listaZakupow)
{
date = DateTime.Now;
this.listaZakupow = listaZakupow;
}
public void Finalizuj(Historia historia)
{
//NOT! - Thread Safe
// EXCEPTION!
// NotSupportedException
// This type of CollectionView does not support changes SourceCollection collection from a thread other than the Dispatcher.
historia.Add(this);
}
private void DoDispatchedAction(Action action)
{
if (currentDispatcher.CheckAccess())
{
action.Invoke();
}
else
{
currentDispatcher.Invoke(DispatcherPriority.DataBind, action);
}
}
}
And when I don't run thread (historyThread) I can normal do method 'Finalizuj' many times
but when I run Thread I can't add anything to list (can't run method - 'Finalizuj')
And VS show me exception about:
NonSupportedException was unhandled
This type of CollectionView does not support changes SourceCollection collection from a thread other than the Dispatcher.
I dont really know what i do wrong.
What i need to add to my project?
In short:
In main Thread - I have object_1 (typeof: ObservableCollection)
In second Thread - I want add anothrer object (typeof: Wpis : ObservableCollection) to object_1
but I get the abovementioned exception
The Exception is telling you exactly what you need to do. You cannot modify the UI from a different thread. I'm assuming that your Observable collection is bound somewhere in your XAML.
You will need to obtain a reference to your MainWindow class so you can access the Dispatcher thread.
public static MainWindow Current { get; private set; }
public MainWindow()
{
Current = this;
//...
}
Then use the Dispatcher thread to modify your collection:
MainWindow.Current.Dispatcher.Invoke((Action)(() =>
{
historia.Add(this);
}));
I have a global variable called:
string tweet;
I run several background workers, that does nothing but wait on value change of the tweet variable. Then run a function called: ProcessTweet( object sender, MyCustomEventArgs args )
My question is what is the best way to handle the property changed event from all those background workers, and later process the results based on the tweet value and another argument passed to the ProcessTweet function.
I tried to take a look at INotifyPropertyChanged but I am not sure how to handle OnValueChange event from each background worker. Will it run the same ProcessTweet function once or each background worker will run an instance of that function?
EDIT:
private ITweet _LastTweet;
public ITweet LastTweet
{
get { return this._LastTweet; }
set
{
this._LastTweet = value;
}
}
Still not sure how to handle property change event the best way ^
And below is the rest of the code
private void bgworker_DoWork(object sender, DoWorkEventArgs e)
{
MyCustomClass myCustomClass = e.Argument as MyCustomClass;
//here I want to listen on the LastTweet Value Change event and handle it
}
List<BackgroundWorker> listOfBGWorkers = new List<BackgroundWorker>();
private BackgroundWorker CreateBackgroundWorker()
{
BackgroundWorker bgworker = new BackgroundWorker();
//add the DoWork etc..
bgworker.DoWork += new System.ComponentModel.DoWorkEventHandler(bgworker_DoWork);
return bgworker;
}
private void buttonStart_Click(object sender, EventArgs e)
{
for (int i = 0; i < 10; i++)
{
//Create the background workers
var bgworker = CreateBackgroundWorker();
listOfBGWorkers.Add(bgworker);
//get the MYCustomClass value;
var myCustomClass = SomeFunction();
bgworker.RunWorkerAsync(myCustomClass);
}
}
Ok - here's a small console app that demonstrates what I think you're trying to do.
It creates a 'source of tweets' in a thread.
You can subscribe to this 'source' and be notified when a new tweet 'arrives'.
You create TweetHandlers which have internal queues of tweets to process
You subscribe these TweetHandlers to the source
When a new tweet arrives, it is added to the queues of all the TweetHandlers by the event subscription
The TweetHandlers are set to run in their own Tasks. Each TweetHandler has its own delegate for performing a customizable action on a Tweet.
The code is as follows:
interface ITweet
{
object someData { get; }
}
class Tweet : ITweet
{
public object someData { get; set; }
}
class TweetSource
{
public event Action<ITweet> NewTweetEvent = delegate { };
private Task tweetSourceTask;
public void Start()
{
tweetSourceTask = new TaskFactory().StartNew(createTweetsForever);
}
private void createTweetsForever()
{
while (true)
{
Thread.Sleep(1000);
var tweet = new Tweet{ someData = Guid.NewGuid().ToString() };
NewTweetEvent(tweet);
}
}
}
class TweetHandler
{
public TweetHandler(Action<ITweet> handleTweet)
{
HandleTweet = handleTweet;
}
public void AddTweetToQueue(ITweet tweet)
{
queueOfTweets.Add(tweet);
}
public void HandleTweets(CancellationToken token)
{
ITweet item;
while (queueOfTweets.TryTake(out item, -1, token))
{
HandleTweet(item);
}
}
private BlockingCollection<ITweet> queueOfTweets = new BlockingCollection<ITweet>();
private Action<ITweet> HandleTweet;
}
class Program
{
static void Main(string[] args)
{
var handler1 = new TweetHandler(TweetHandleMethod1);
var handler2 = new TweetHandler(TweetHandleMethod2);
var source = new TweetSource();
source.NewTweetEvent += handler1.AddTweetToQueue;
source.NewTweetEvent += handler2.AddTweetToQueue;
// start up the task threads (2 of them)!
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
var taskFactory = new TaskFactory(token);
var task1 = taskFactory.StartNew(() => handler1.HandleTweets(token));
var task2 = taskFactory.StartNew(() => handler2.HandleTweets(token));
// fire up the source
source.Start();
Thread.Sleep(10000);
tokenSource.Cancel();
}
static void TweetHandleMethod1(ITweet tweet)
{
Console.WriteLine("Did action 1 on tweet {0}", tweet.someData);
}
static void TweetHandleMethod2(ITweet tweet)
{
Console.WriteLine("Did action 2 on tweet {0}", tweet.someData);
}
}
The output looks like this:
Did action 2 on tweet 892dd6c1-392c-4dad-8708-ca8c6e180907
Did action 1 on tweet 892dd6c1-392c-4dad-8708-ca8c6e180907
Did action 2 on tweet 8bf97417-5511-4301-86db-3ff561d53f49
Did action 1 on tweet 8bf97417-5511-4301-86db-3ff561d53f49
Did action 2 on tweet 9c902b1f-cfab-4839-8bb0-cc21dfa301d5
When using the StartNew() method to kick off a process on a new thread, I need to figure out how to make another call into this object in that same thread (I assume this would be some sort of Join operation?).
The following example is dumbed down to illustrate the meat of what I am trying to do. I am well aware it is severely lacking in basic concurrency considerations. But I didn't want to cloud the code with all of that logic, so please forgive me on that.
The following console app shows what I am trying to accomplish. Assume on the StartNew() call a new thread with ID 9976 is created and the method invoked there. I would like the subsequent call to ProcessImmediate() in the file system watcher change event handler to be made on thread 9976 as well. As it stands, the call would share the same thread that is used for the file system watcher change event.
Can this be done, and if so, how?
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var runner = new Runner();
runner.Run();
Console.ReadKey();
}
}
public class Runner
{
private Activity _activity = null;
private FileSystemWatcher _fileSystemWatcher;
public void Run()
{
_activity = new Activity();
// start activity on a new thread
Task.Factory.StartNew(() => _activity.Go());
_fileSystemWatcher = new FileSystemWatcher();
_fileSystemWatcher.Filter = "*.watcher";
_fileSystemWatcher.Path = "c:\temp";
_fileSystemWatcher.Changed += FileSystemWatcher_Changed;
_fileSystemWatcher.EnableRaisingEvents = true;
}
private void FileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
{
// WANT TO CALL THIS FOR ACTIVITY RUNNING ON PREVIOUSLY CALLED THREAD
_activity.ProcessImmediate();
}
}
public class Activity
{
public void Go()
{
while (!Stop)
{
// for purposes of this example, magically assume that ProcessImmediate has not been called when this is called
DoSomethingInteresting();
System.Threading.Thread.Sleep(2000);
}
}
protected virtual void DoSomethingInteresting() { }
public void ProcessImmediate()
{
// for purposes of this example, assume that Go is magically in its sleep state when ProcessImmediate is called
DoSomethingInteresting();
}
public bool Stop { get; set; }
}
}
* UPDATE *
Thanks for the excellent responses. I took Mike's suggestion and implemented it for my console app. Below is the full working code which also includes the use of a cancellation token. I post this in case someone else might find it useful.
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var runner = new Runner();
runner.Run();
Console.ReadKey();
runner.Stop();
Console.ReadKey();
}
}
public class Runner
{
private Activity _activity = null;
private FileSystemWatcher _fileSystemWatcher;
private CancellationTokenSource _cts = new CancellationTokenSource();
public void Stop() { _cts.Cancel(); }
public void Run()
{
_activity = new Activity();
// start activity on a new thread
var task = new Task(() => _activity.Go(_cts.Token), _cts.Token, TaskCreationOptions.LongRunning);
task.Start();
_fileSystemWatcher = new FileSystemWatcher();
_fileSystemWatcher.Filter = "*.watcher";
_fileSystemWatcher.Path = "C:\\Temp\\FileSystemWatcherPath";
_fileSystemWatcher.Changed += FileSystemWatcher_Changed;
_fileSystemWatcher.EnableRaisingEvents = true;
}
private void FileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
{
// WANT TO CALL THIS FOR ACTIVITY RUNNING ON PREVIOUSLY CALLED THREAD
_activity.ProcessImmediate();
}
}
public class Activity : IDisposable
{
private AutoResetEvent _processing = new AutoResetEvent(false);
public void Go(CancellationToken ct)
{
Thread.CurrentThread.Name = "Go";
while (!ct.IsCancellationRequested)
{
// for purposes of this example, magically assume that ProcessImmediate has not been called when this is called
DoSomethingInteresting();
_processing.WaitOne(5000);
}
Console.WriteLine("Exiting");
}
protected virtual void DoSomethingInteresting()
{
Console.WriteLine(string.Format("Doing Something Interesting on thread {0}", Thread.CurrentThread.ManagedThreadId));
}
public void ProcessImmediate()
{
// for purposes of this example, assume that Go is magically in its sleep state when ProcessImmediate is called
_processing.Set();
}
public void Dispose()
{
if (_processing != null)
{
_processing.Dispose();
_processing = null;
}
}
}
}
First, you should use TaskCreationOptions.LongRunning if you are creating a task that will not complete quickly. Second, use an AutoResetEvent to signal the waiting thread to wake up. Note that below ProcessImmediate will return before DoSomethingInteresting has completed running on the other thread. Example:
using System.Threading;
public class Activity : IDisposable
{
private AutoResetEvent _processing = new AutoResetEvent(false);
public void Go()
{
while (!Stop)
{
// for purposes of this example, magically assume that ProcessImmediate has not been called when this is called
DoSomethingInteresting();
_processing.WaitOne(2000);
}
}
protected virtual void DoSomethingInteresting() { }
public void ProcessImmediate()
{
_processing.Set();
}
public bool Stop { get; set; }
public void Dispose()
{
if (_processing != null)
{
_processing.Dispose();
_processing = null;
}
}
}
User mike has given a better solution, which will be appropriate when you like to call the same method immediately. If you want to call a different methods immediately I'll expand mike's answer to achieve that.
using System.Threading;
public class Activity : IDisposable
{
private AutoResetEvent _processing = new AutoResetEvent(false);
private ConcurrentQueue<Action> actionsToProcess = new ConcurrentQueue<Action>();
public void Go()
{
while (!Stop)
{
// for purposes of this example, magically assume that ProcessImmediate has not been called when this is called
DoSomethingInteresting();
_processing.WaitOne(2000);
while(!actionsToProcess.IsEmpty)
{
Action action;
if(actionsToProcess.TryDeque(out action))
action();
}
}
}
protected virtual void DoSomethingInteresting() { }
public void ProcessImmediate(Action action)
{
actionsToProcess.Enqueue(action);
_processing.Set();
}
public bool Stop { get; set; }
public void Dispose()
{
if (_processing != null)
{
_processing.Dispose();
_processing = null;
}
}
}
To execute different methods on the same thread you can use a message loop that dispatches incoming requests. A simple option would be to use the event loop scheduler of the Reactive Extensions and to "recursively" schedule your Go() function - if in the mean time a different operation is scheduled it would be processed before the next Go() operation.
Here is a sample:
class Loop
: IDisposable
{
IScheduler scheduler = new EventLoopScheduler();
MultipleAssignmentDisposable stopper = new MultipleAssignmentDisposable();
public Loop()
{
Next();
}
void Next()
{
if (!stopper.IsDisposed)
stopper.Disposable = scheduler.Schedule(Handler);
}
void Handler()
{
Thread.Sleep(1000);
Console.WriteLine("Handler: {0}", Thread.CurrentThread.ManagedThreadId);
Next();
}
public void Notify()
{
scheduler.Schedule(() =>
{
Console.WriteLine("Notify: {0}", Thread.CurrentThread.ManagedThreadId);
});
}
public void Dispose()
{
stopper.Dispose();
}
}
static void Main(string[] args)
{
using (var l = new Loop())
{
Console.WriteLine("Press 'q' to quit.");
while (Console.ReadKey().Key != ConsoleKey.Q)
l.Notify();
}
}
In it's simplicity what I am trying to do is handle "Doing Something" by firing off a process on a seperate thread to do what I need to do and waiting for an event to be raised to say "I have finished doing what I need to do". In the EventArgs though I will have a property for any errors which may be encountered during the process. Here is a simplified example of my situation.
public class MessageHandler
{
private AutoResetEvent MessageHasSent = new AutoResetEvent(false);
public void SendMessage()
{
MessageSender ms = new MessageSender();
ms.MessageSent += new EventHandler<MessageSentEventArgs>(MessageHandler_MessageSent);
Thread t = new Thread(ms.Send());
t.Start();
MessageHasSent.WaitOne();
//Do some check here
//Same again but for "Message recieved"
}
void MessageHandler_MessageSent(object sender, MessageSentEventArgs e)
{
if (e.Errors.Count != 0)
{
//What can I do here to return to the next step after waitone?
}
else
MessageHasSent.Set();
}
}
public class MessageSender
{
public event EventHandler<MessageSentEventArgs> MessageSent;
public void Send()
{
//Do some method which could potentiallialy return a List<Error>
MessageSent(this, new MessageSentEventArgs() { Errors = new List<Error>() });
}
}
public class Error { }
public class MessageSentEventArgs : EventArgs
{
public List<Error> Errors;
}
Essentially once the event has been raised from Send the code will continute, however I want some way of the event giving feedback, potentially using the MessageHasSent. I have tried different methods, I thought if I called Close instead of Set it would perhaps allow me to access something such as IsClosed. You could throw an exception or set a flag outside of the scope of the event to check but I feel like this is dirty.
Any suggestions?
Using the TPL isn't applicable in my case as I am using .NET 3.5.
Since it seems that this entire section of code is already running in a background thread, and you're doing nothing more than starting up a new thread just so that you can wait for it to finish, you'd be better off just calling Send directly, rather than asynchronously.
You don't need to fire off an event when you're completed.
You don't need to signal the main thread when it needs to continue.
You don't need to log the exceptions in a List, you can just throw them and catch them in SendMessage with a try/catch block.
This will do what you want:
public class MessageHandler
{
private AutoResetEvent MessageHasSent = new AutoResetEvent(false);
private bool IsSuccess = false;
public void SendMessage()
{
MessageSender ms = new MessageSender();
ms.MessageSent += new EventHandler<MessageSentEventArgs>(MessageHandler_MessageSent);
Thread t = new Thread(ms.Send());
t.Start();
MessageHasSent.WaitOne();
if(IsSuccess)
//wohooo
else
//oh crap
//Same again but for "Message recieved"
}
void MessageHandler_MessageSent(object sender, MessageSentEventArgs e)
{
IsSuccess = e.Errors.Count == 0;
MessageHasSent.Set();
}
}
public class MessageSender
{
public event EventHandler<MessageSentEventArgs> MessageSent;
public void Send()
{
//Do some method which could potentiallialy return a List<Error>
MessageSent(this, new MessageSentEventArgs() { Errors = new List<Error>() });
}
}
public class Error { }
public class MessageSentEventArgs : EventArgs
{
public List<Error> Errors;
}
I am trying to create a thread which will continuously check for changes to a value, then visually show that change in a PictureBox located in my GUI.
What I actually wrote is a bit more complicated, so I simplified it while keeping the basic idea, I would be happy to provide clarification if this isn't enough:
public class CheckPictures
{
PictureBox update;
List<String> check;
public CheckPictures(PictureBox anUpdate, List<String> aCheck)
{
update = anUpdate;
check = aCheck;
}
public void start()
{
while(true)
{
if (aCheck[0] == "Me")
{
update.Image = Image.fromFile("");
}
}
}
}
static int Main(string[] args)
{
List<String> picturesList = new List<String>();
CheckPictures thread1 = new CheckPictures(PictureBox1, picturesList);
Thread oThread1 = new Thread(thread1.start));
}
What I want it to do is dynamically change the picture in PictureBox1 if I were to add the string "Me" to pictureList. The above code isn't working like I'd hoped. I had thought that by passing the actual PictureBox and List, any changes to the List elsewhere is the program would be caught by the thread. So my first question is: Is this possible? And if so, what change would I need to make to my code to achieve it?
You might want to use events. You register an eventhandler and when something changes in one thread it calls an event handler in the other to do the work. Busy waiting wastes cpu.
You definetely do not want to do an infinite loop, this will just consume cpu:
while(true)
{
if (aCheck[0] == "Me")
{
update.Image = Image.fromFile("");
}
}
I think you should look into the CountdownLatch class.
public class CountdownLatch
{
private int m_remain;
private EventWaitHandle m_event;
public CountdownLatch(int count)
{
m_remain = count;
m_event = new ManualResetEvent(false);
}
public void Signal()
{
// The last thread to signal also sets the event.
if (Interlocked.Decrement(ref m_remain) == 0)
m_event.Set();
}
public void Wait()
{
m_event.WaitOne();
}
}
The basic idea here is that you need to stop execution on your thread for some time and resume whenever a certain condition has been met (perhaps on another thread).
In other words, you will have a counter, decrement its value on certain condition and whenever it goes to zero you fire your event, execute some code and then start over (stop execution and wait for the counter to go to zero).
In your case you could set the counter to 1 and decrement its value whenever you've set aCheck[0] = "Me"; This way you don't waste CPU.
Pseudo code:
Initialize counter:
CountdownLatch latch = new CountdownLatch(1);
Make thread wait:
public void start()
{
while(true)
{
latch.Wait(); //execution stops
{
//execution resumes once the latch counter is zero.
if (aCheck[0] == "Me") //double check you have what you need
{
update.Image = Image.fromFile("");
latch = new CountdownLatch(1); //reset if you need to do it again
}
}
}
}
Whenever your condition is met (i.e. aCheck[0] = "Me";) signal your latch:
latch.Signal();
this last line will make the thread resume execution. Good stuff.
Create some object, which will raise event, when new picture was added. E.g. class representing pictures collection:
public class PicturesCollection
{
public event EventHandler<PictureAddedEventArgs> PictureAdded;
private List<string> _pictures = new List<string>();
public void Add(string name)
{
_pictures.Add(name);
if (PictureAdded != null)
PictureAdded(this, new PictureAddedEventArgs(name));
}
public IEnumerable<string> Pictures
{
get { return _pictures; }
}
}
If you want to provide some additional data to event, create custom EventArgs:
public class PictureAddedEventArgs : EventArgs
{
public PictureAddedEventArgs(string name)
{
Name = name;
}
public string Name { get; private set; }
}
All you need now - create pictures collection and subscribe to that event:
static int Main(string[] args)
{
PicturesCollection pictures = new PicturesCollection();
pictures.PictureAdded += Pictures_PictureAdded;
}
static void Pictures_PictureAdded(object sender, PictureAddedEventArgs e)
{
if (e.Name == "Me")
PictureBox1.Image = Image.fromFile("");
}
If you add somewhere in your application new picture to collection, it will raise PictureAdded event, which you can handle and update PictureBox. CPU is not wasted in this case.