I have a message bus class which use Rx to push multiple threads events in WPF application.
My problem ObserveOnDispatcher does not invoke the event handler in the UI thread.
Code:
private void button_Click(object sender, RoutedEventArgs e)
{
var messageBus = new MessageBus();
messageBus.GetMessages<Message>().ObserveOnDispatcher().Subscribe(x => TestHanlder(x));
Trace.WriteLine("Main Thread Id:" + Thread.CurrentThread.ManagedThreadId);
var deviceManager = new DeviceManager(messageBus);
deviceManager.Start();
}
private void TestHanlder(Message message)
{
Trace.WriteLine("UI Handler ThreadId:" + Thread.CurrentThread.ManagedThreadId);
}
public class DeviceManager
{
private readonly MessageBus _messageBus;
public DeviceManager(MessageBus messageBus)
{
_messageBus = messageBus;
}
public void Start()
{
for (;;)
{
var t = Task.Factory.StartNew(() => BackGroundTask(), TaskCreationOptions.LongRunning);
t.Wait();
}
}
private void BackGroundTask()
{
Thread.Sleep(1000);
Trace.WriteLine("Push ThreadId:" + Thread.CurrentThread.ManagedThreadId);
var message = new Message();
_messageBus.Publish(message);
}
}
public class MessageBus
{
readonly ISubject<object> _messages;
public MessageBus()
{
_messages = new Subject<object>();
}
public void Publish<TMessage>(TMessage message)
{
_messages.OnNext(message);
}
public IObservable<TMessage> GetMessages<TMessage>()
{
return _messages.OfType<TMessage>();
}
}
public class Message
{
public Message()
{
}
}
Without ObserveOnDispatcher:
messageBus.GetMessages<Message>().Subscribe(x => TestHanlder(x));
..........................Output.................................
Main Thread Id:8
Push ThreadId:9
UI Handler ThreadId:9
But I need to execute the TestHanlder function in the main thread or UI thread, in my use case above it must be the thread number 8.
When I use ObserveOnDispatcher:
messageBus.GetMessages<Message>().ObserveOnDispatcher().Subscribe(x => TestHanlder(x));
..........................Output.................................
Main Thread Id:9
Push ThreadId:10
------------------> UI Handler ThreadId: are missing not there!?
What I'm doing wrong here?!!!!!
for (;;) { t.Wait(); } this code is executed in the UI thread and prevents it from executing anything else dispatched on it. ObserveOnDispatcher is working fine, but your dispatcher thread is blocked.
If you introduce async/await (that will release the thread), the scenario will work fine:
private async void button_Click(object sender, RoutedEventArgs e)
{
var messageBus = new MessageBus();
messageBus.GetMessages<Message>().ObserveOnDispatcher().Subscribe(x => TestHanlder(x));
Trace.WriteLine("Main Thread Id:" + Thread.CurrentThread.ManagedThreadId);
var deviceManager = new DeviceManager(messageBus);
await deviceManager.Start();
}
...
public async Task<Unit> Start()
{
for (;;)
{
await Task.Factory.StartNew(() => BackGroundTask(), TaskCreationOptions.LongRunning);
}
}
Related
I am creating a WPF app where I want to have a global bool im assuming, on the first button click I’ll set this bool to true and I want it to run a task (continuously call an API method) until I click the button again and it stops it. What would be the best way to do this?
private bool running = false;
private async void BtnTrade1_Buy_Click(object sender, RoutedEventArgs e)
{
if (!running)
{
running = true;
}
else
running = false;
if (running)
{
RunningNrunnin(running);
//tradeClient.GetTradeHistory();
}
}
public void RunningNrunnin(bool running)
{
if (running)
{
Task task = new Task(() =>
{
while (running)
{
GetTradeHistory();
Thread.Sleep(2000);
}
});
task.Start();
}
}
Added Below
I would like to call a method over and over until the user creates a cancel request on a thread in the background. I currently had it so I can call a action (a counter) and update the GUI each second but when I try to do this same thing with a method call it executes only once.
// Here is the method I want to call continously until canceled
private async void HistoryTest()
{
cancellationToken = new CancellationTokenSource();
task = Task.Factory.StartNew(async () =>
{
while (true)
{
cancellationToken.Token.ThrowIfCancellationRequested();
await Client2.GetHistory();
await Task.Delay(2000);
}
}, cancellationToken.Token);
}
public async Task GetHistory()
{
try
{
var response = await Client.Service.GetDataAsync
(
ProductType.BtcUsd,
5,
1
);
}
catch(Exception)
{
throw;
}
}
I made a little console test app to test this so I had to change the method signatures (static) and can't use ButtonClick on a console. I simulated the button click by putting as sleep between the programatic "button click".
This might get you started.
private static bool isRunning = false;
private static int clickCounter = 0;
private static int iterationsCounter = 0;
static void Main(string[] args)
{
Console.WriteLine(“Start”);
for(int i = 0; i < 7; i++)
{
BtnTrade1_Buy_Click();
System.Threading.Thread.Sleep(1000);
}
Console.WriteLine(“END”);
}
private static async Task BtnTrade1_Buy_Click()
{
iterationsCounter = 0;
isRunning = !isRunning;
Console.WriteLine($"Ha: {isRunning} {clickCounter++}");
await RunningNrunnin();
}
private static async Task RunningNrunnin()
{
await Task.Run(() => Runit());
}
private static void Runit()
{
while (isRunning)
{
GetTradeHistory();
System.Threading.Thread.Sleep(100);
}
}
private static void GetTradeHistory()
{
Console.WriteLine($"Hello Test {iterationsCounter++}");
}
Of course you wouldn't need all the counters and the Console.WriteLine() stuff. They are there to allow you to visualize what is happening.
Let me know if you need more info.
You don't need to do anything else inside the BtnTrade1_Buy_Click event handler, beyond toggling the isRunning field:
private bool _isRunning;
private void BtnTrade1_Buy_Click(object sender, RoutedEventArgs e)
{
_isRunning = !_isRunning;
}
The Task that is getting the trade history in a loop, needs to be started only once. You could start it in the Window_Loaded event. Storing the Task in a private field is a good idea, in case you decide to await it at some point, but if you are handling the exceptions inside the task it's not necessary.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
_ = StartTradeHistoryLoopAsync(); // Fire and forget
}
private async Task StartTradeHistoryLoopAsync()
{
while (true)
{
var delayTask = Task.Delay(2000);
if (_isRunning)
{
try
{
await Task.Run(() => GetTradeHistory()); // Run in the ThreadPool
//GetTradeHistory(); // Alternative: Run in the UI thread
}
catch (Exception ex)
{
// Handle the exception
}
}
await delayTask;
}
}
Don't forget to stop the task when the window is closed.
private void Window_Closed(object sender, EventArgs e)
{
_isRunning = false;
}
This will stop the calls to GetTradeHistory(), but will not stop the loop. You may need to add one more private bool field to control the loop itself:
while (_alive) // Instead of while (true)
I'm trying to find some solutions to my problem here, but with no result (or I just do not get them right) so if anyone could help / explain i will be really gratefull.
I'm just developing a tool for system administrators using Win Form and now I need to create a continuous ping on the selected machine which is running on the background. There is an indicator for Online status on UI which I need to edit with background ping. So right now I'm in this state:
Class A (Win form):
ClassB activeRelation = new ClassB();
public void UpdateOnline(Relation pingedRelation)
{
//There is many Relations at one time, but form shows Info only for one...
if (activeRelation == pingedRelation)
{
if (p_Online.InvokeRequired)
{
p_Online.Invoke(new Action(() =>
p_Online.BackgroundImage = (pingedRelation.Online) ? Properties.Resources.Success : Properties.Resources.Failure
));
}
else
{
p_Online.BackgroundImage = (pingedRelation.Online) ? Properties.Resources.Success : Properties.Resources.Failure;
}
}
}
//Button for tunring On/Off the background ping for current machine
private void Btn_PingOnOff_Click(object sender, EventArgs e)
{
Button btn = (sender is Button) ? sender as Button : null;
if (btn != null)
{
if (activeRelation.PingRunning)
{
activeRelation.StopPing();
btn.Image = Properties.Resources.Switch_Off;
}
else
{
activeRelation.StartPing(UpdateOnline);
btn.Image = Properties.Resources.Switch_On;
}
}
}
Class B (class thats represent relation to some machine)
private ClassC pinger;
public void StartPing(Action<Relation> action)
{
pinger = new ClassC(this);
pinger.PingStatusUpdate += action;
pinger.Start();
}
public void StopPing()
{
if (pinger != null)
{
pinger.Stop();
pinger = null;
}
}
Class C (background ping class)
private bool running = false;
private ClassB classb;
private Task ping;
private CancellationTokenSource tokenSource;
public event Action<ClassB> PingStatusUpdate;
public ClassC(ClassB classB)
{
this.classB = classB;
}
public void Start()
{
tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;
ping = PingAction(token);
running = true;
}
public void Stop()
{
if (running)
{
tokenSource.Cancel();
ping.Wait(); //And there is a problem -> DeadLock
ping.Dispose();
tokenSource.Dispose();
}
running = false;
}
private async Task PingAction(CancellationToken ct)
{
bool previousResult = RemoteTasks.Ping(classB.Name);
PingStatusUpdate?.Invoke(classB);
while (!ct.IsCancellationRequested)
{
await Task.Delay(pingInterval);
bool newResult = RemoteTasks.Ping(classB.Name);
if (newResult != previousResult)
{
previousResult = newResult;
PingStatusUpdate?.Invoke(classB);
}
}
}
So the problem is in deadlock when I cancel token and Wait() for task to complete -> it's still running, but While(...) in task is finished right.
You have a deadlock because ping.Wait(); blocks UI thread.
You should wait for task asynchronously using await.
So, if Stop() is event handler then change it to:
public async void Stop() // async added here
{
if (running)
{
tokenSource.Cancel();
await ping; // await here
ping.Dispose();
tokenSource.Dispose();
}
running = false;
}
If it is not:
public async Task Stop() // async added here, void changed to Task
{
if (running)
{
tokenSource.Cancel();
await ping; // await here
ping.Dispose();
tokenSource.Dispose();
}
running = false;
}
As mentioned by #JohnB async methods should have Async suffix so, the method should be named as StopAsync().
Similar problem and solution are explained here - Do Not Block On Async Code
You should avoid synchronous waiting on tasks, so you should always use await with tasks instead of Wait() or Result. Also, as pointed by #Fildor you should use async-await all the way to avoid such situations.
So a simple application that has a button to get data from a server and then updates a Text in UI.
In addition, I want to start another thread that again gets the data every 3 seconds.
Is the below code the right way to create a thread and update the UI (binding Value)?
Has the usage of the delegate NoArgDelegate a drawback in this scenario? Or is a bad idea to pass an async method in the delegate? I am still trying to get the concept of delegates and Dispatcher.
private delegate void NoArgDelegate();
public IAsyncCommand GetDataCommand { get; private set; } // binding for a "Get data" button
public JustAViewModel()
{
// ...
GetDataCommand = AsyncCommand.Create(() => GetDataAsync());
var fetcher = new NoArgDelegate(ContinuouslyFetchData);
fetcher.BeginInvoke(null, null);
}
public string Value // in xaml: TextValue="{Binding Value}"
{
get => _value;
set
{
if (_value != value)
{
_value = value;
RaisePropertyChanged("Value");
}
}
}
private async void ContinuouslyFetchData()
{
while (true)
{
System.Threading.Thread.Sleep(3000);
await GetDataAsync();
}
}
private async Task<string> GetDataAsync()
{
Value = await Task.Run(() => DataProvider.GetData());
return Value;
}
You have misunderstood what BeginInvoke does. It does not create a new thread.
However, you should not create a thread anyway. For a cyclically repeated action, use a timer.
I'd recommend a DispatcherTimer, with an async Tick event handler:
private readonly DispatcherTimer timer;
public JustAViewModel()
{
timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(3) };
timer.Tick += TimerTick;
timer.Start();
}
private async void TimerTick(object sender, EventArgs e)
{
Value = await Task.Run(() => DataProvider.GetData());
}
I want my program to wait after below line
frmProgressBarObj = PullMSI.ExtractByMSIName("products.txt", false);
as above method is internally calling thread through StartProcessWithProgress() method . I want that thread to be completed before //code logic -2 line gets executed. At the same time, It should not stop UI update done by frmProgressBar.UpdateProgress(). How do I do this?
namespace NS1
{
public partial class frmMain : Form
{
private void button1_Click(object sender, EventArgs e)
{
frmProgressBar frmProgressBarObj = PullMSI.ExtractByMSIName("products.txt", false);
//code logic - 2
MessageBox.Show("This is executing immediately.
I want to wait until above thread is complete");
}
}
public partial class frmProgressBar : Form
{
public void UpdateProgress(String strTextToDisplayOnProgress)
{
progressBar1.BeginInvoke(
new Action(() =>
{
progressBar1.Value++;
lblFileName.Text = strTextToDisplayOnProgress;
if (progressBar1.Value == progressBar1.Maximum)
{
this.Hide();
}
}));
}
public delegate void DelProgress();
public void StartProcessWithProgress(DelProgress delMethodCode, int maxCount)
{
InitializeProgress(maxCount);
Thread backgroundThread = new Thread(new ThreadStart(delMethodCode));
backgroundThread.Start();
}
}
public static class PullMSI
{
public static frmProgressBar ExtractByMSIName(String strProductFilePath, bool reNameMSI)
{
frmProgressBar frmProgressBar = new frmProgressBar();
frmProgressBar.StartProcessWithProgress(() =>
{
//StreamRader sr declaration and other code
while (!sr.EndOfStream)
{
//logic here
frmProgressBar.UpdateProgress("Copying sr.msiname");
}
}, 2);
return frmProgressBar;
}
}
}
I'm very surprised you haven't worked with any of these before but I would really recommend reading about threading in C# since it's fundamentally important to understand the intricacies and learning the language.
Below are three different ways you can achieve what you want:
1. Using reset events (further reading: https://msdn.microsoft.com/en-us/library/system.threading.manualreseteventslim(v=vs.110).aspx). If your C# version doesn't have the ManualResetEventSlim, replace it with ManualResetEvent and change Wait() with WaitOne()
class LockingWithResetEvents
{
private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false);
public void Test()
{
MethodUsingResetEvents();
}
private void MethodUsingResetEvents()
{
ThreadPool.QueueUserWorkItem(_ => DoSomethingLong());
ThreadPool.QueueUserWorkItem(_ => ShowMessageBox());
}
private void DoSomethingLong()
{
Console.WriteLine("Doing somthing.");
Thread.Sleep(1000);
_resetEvent.Set();
}
private void ShowMessageBox()
{
_resetEvent.WaitOne();
Console.WriteLine("Hello world.");
}
}
2) Using Task Parallel Library (TPL). Further reading: https://msdn.microsoft.com/en-us/library/dd460717(v=vs.110).aspx
class LockingWithTPL
{
public void Test()
{
Task.Factory.StartNew(DoSomethingLong).ContinueWith(result => ShowMessageBox());
}
private void DoSomethingLong()
{
Console.WriteLine("Doing somthing.");
Thread.Sleep(1000);
}
private void ShowMessageBox()
{
Console.WriteLine("Hello world.");
}
}
3) Using Async/Await. Further reading: https://msdn.microsoft.com/en-us/library/hh191443.aspx
class LockingWithAwait
{
public void Test()
{
DoSomething();
}
private async void DoSomething()
{
await Task.Run(() => DoSomethingLong());
ShowMessageBox();
}
private async void DoSomethingLong()
{
Console.WriteLine("Doing somthing.");
Thread.Sleep(10000);
}
private void ShowMessageBox()
{
Console.WriteLine("Hello world.");
}
}
Also good to know: Mutex (https://msdn.microsoft.com/en-us/library/system.threading.mutex(v=vs.110).aspx), Semaphore (https://msdn.microsoft.com/en-us/library/system.threading.semaphore(v=vs.110).aspx), Lock (https://msdn.microsoft.com/en-us/library/c5kehkcz.aspx), SemaphoreSlim (https://msdn.microsoft.com/en-us/library/system.threading.semaphoreslim(v=vs.110).aspx), Monitor (https://msdn.microsoft.com/en-us/library/system.threading.monitor(v=vs.110).aspx) and Interlocked (https://msdn.microsoft.com/en-us/library/system.threading.interlocked(v=vs.110).aspx).
If you're using .NET 4.0 (with VS2012) or above, you can do this quite easily with the Task Parallel Library and async-await:
private async void button1_Click(object sender, EventArgs e)
{
frmProgressBar frmProgressBarObj = await Task.Run(() =>
PullMSI.ExtractByMSIName("products.txt", false));
MessageBox.Show(string.Format("Returned {0}", frmProgressBarObj.ToString());
}
For .NET 4, you'll need to add Microsoft.Bcl.Async.
I have a form that starts a thread. Now I want the form to auto-close when this thread terminates.
The only solution I found so far is adding a timer to the form and check if thread is alive on every tick. But I want to know if there is a better way to do that?
Currently my code looks more less like this
partial class SyncForm : Form {
Thread tr;
public SyncForm()
{
InitializeComponent();
}
void SyncForm_Load(object sender, EventArgs e)
{
thread = new Thread(new ThreadStart(Synchronize));
thread.IsBackground = true;
thread.Start();
threadTimer.Start();
}
void threadTimer_Tick(object sender, EventArgs e)
{
if (!thread.IsAlive)
{
Close();
}
}
void Synchronize()
{
// code here
}
}
The BackgroundWorker class exists for this sort of thread management to save you having to roll your own; it offers a RunWorkerCompleted event which you can just listen for.
Edit to make it call a helper method so it's cleaner.
thread = new Thread(() => { Synchronize(); OnWorkComplete(); });
...
private void OnWorkComplete()
{
Close();
}
If you have a look at a BackgroundWorker, there is a RunWorkerCompleted event that is called when the worker completes.
For more info on BackgroundWorkers Click Here
Or
You could add a call to a complete function from the Thread once it has finished, and invoke it.
void Synchronize()
{
//DoWork();
//FinishedWork();
}
void FinishedWork()
{
if (InvokeRequired == true)
{
//Invoke
}
else
{
//Close
}
}
Have a look at delegates, IAsyncResult, BeginInvoke and AsyncCallback
At the end of your thread method, you can call Close() using the Invoke() method (because most WinForms methods should be called from the UI thread):
public void Synchronize()
{
Invoke(new MethodInvoker(Close));
}
Solution for arbitrary thread (e.g. started by some other code), using UnmanagedThreadUtils package:
// Use static field to make sure that delegate is alive.
private static readonly UnmanagedThread.ThreadExitCallback ThreadExitCallbackDelegate = OnThreadExit;
public static void Main()
{
var threadExitCallbackDelegatePtr = Marshal.GetFunctionPointerForDelegate(ThreadExitCallbackDelegate);
var callbackId = UnmanagedThread.SetThreadExitCallback(threadExitCallbackDelegatePtr);
for (var i = 1; i <= ThreadCount; i++)
{
var threadLocalVal = i;
var thread = new Thread(_ =>
{
Console.WriteLine($"Managed thread #{threadLocalVal} started.");
UnmanagedThread.EnableCurrentThreadExitEvent(callbackId, new IntPtr(threadLocalVal));
});
thread.Start();
}
UnmanagedThread.RemoveThreadExitCallback(callbackId);
}
private static void OnThreadExit(IntPtr data)
{
Console.WriteLine($"Unmanaged thread #{data.ToInt64()} is exiting.");
}