C# Triggering event from new task [duplicate] - c#

This question already has answers here:
Why is the console window closing immediately once displayed my output?
(15 answers)
Closed 1 year ago.
I wanted to test if I can trigger an event using method working in a new task.
when I do this:
using System;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
subscriber f = new subscriber();
}
}
class subscriber
{
publisher x;
public subscriber()
{
x = new publisher();
x.ThresholdReached += c_ThresholdReached;
x.method2();
}
static void c_ThresholdReached(object sender, EventArgs e)
{
Console.WriteLine("The threshold was reached.");
}
}
class publisher
{
public event EventHandler ThresholdReached;
public publisher()
{
}
public void method1()
{
OnThresholdReached(EventArgs.Empty);
}
public void method2()
{
Task.Run(() => method1());
}
protected virtual void OnThresholdReached(EventArgs e)
{
EventHandler handler = ThresholdReached;
handler?.Invoke(this, e);
}
}
}
The output is nothing!
However, when I do this:
using System;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
subscriber f = new subscriber();
}
}
class subscriber
{
publisher x;
public subscriber()
{
x = new publisher();
x.ThresholdReached += c_ThresholdReached;
x.method2();
}
static void c_ThresholdReached(object sender, EventArgs e)
{
Console.WriteLine("The threshold was reached.");
}
}
class publisher
{
public event EventHandler ThresholdReached;
public publisher()
{
}
public void method1()
{
OnThresholdReached(EventArgs.Empty);
}
public void method2()
{
//Here is the change
method1();
Task.Run(() => method1());
}
protected virtual void OnThresholdReached(EventArgs e)
{
EventHandler handler = ThresholdReached;
handler?.Invoke(this, e);
}
}
}
the output is this:
The threshold was reached
The threshold was reached
this is strange!
I could not understand why it prints twice.
However, I excepted it will not work with method1 is running in new task, because it will not be working in the same thread
could someone explain why?
and is there a way to communicate a concurrent method with the parent thread?
thanks in advance

As mentioned in comment, your code finishes before the task will run and pring anything. You should modify your code in async manner to await the task to finish
using System;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Program
{
static async Task Main(string[] args)
{
subscriber f = new subscriber();
await f.Test();
}
}
class subscriber
{
publisher x;
public subscriber()
{
x = new publisher();
x.ThresholdReached += c_ThresholdReached;
}
public async Task Test()
{
await x.method2();
}
static void c_ThresholdReached(object sender, EventArgs e)
{
Console.WriteLine("The threshold was reached.");
}
}
class publisher
{
public event EventHandler ThresholdReached;
public publisher()
{
}
public void method1()
{
OnThresholdReached(EventArgs.Empty);
}
public async Task method2()
{
await Task.Run(() => method1());
}
protected virtual void OnThresholdReached(EventArgs e)
{
EventHandler handler = ThresholdReached;
handler?.Invoke(this, e);
}
}
}
As for your second snippet, that looks like the Task has time to run and pring while the main thread printing inside method1. Some times it will pring twice (if have enaught of time) or will print once (if Console.Write in main thread will finish before the task will run).
Additional note: please be aware that your event handler will be called not in main thread, but in the thread in which task is executed (typically, this will be a thread from a thread pool)

When you call Task.Run(...), the action inside Run() will be started in new thread.
In your first case you start the new thread with Task.Run(), but the program finishes and exits before it executes that the new thread.
You can prevent the main thread from terminating by adding Console.Read() at the end of Main(). This way you can wait for the second thread to finish.
class Program
{
static void Main(string[] args)
{
subscriber f = new subscriber();
Console.Read();
}
}
In your second case you will get two messages because of this:
public void method2()
{
method1();
Task.Run(() => method1());
}
This time since you are directly calling method1() you will for sure write the message once in the console. Since printing on the console (any IO in general) is relatively slow, the second thread (created by Task.Run()) will have time to execute, and print the message second time.
Another solution is to create the thread manually
public void method2()
{
Thread t1 = new Thread(() => method1());
t1.IsBackground = false;
t1.Start();
}
The program will not exit until all foreground threads have finished. So when you set IsBackground = false;, the program will wait for that thread

Related

Passing events between threads run from separate class C#

I'm struggling to pass data between a thread started in a separate class from my main form. I believe (I could be wrong) that I should use an event. The problem I have is my subscribers are always null as I call the BluetoothScan class and start the thread before the event is subscribed to:
BluetoothScan bluetoothScan = new BluetoothScan(this);
bluetoothScan.BluetoothDeviceDiscovered += OnBluetoothDeviceDiscovered;
How do I subscribe to the event before starting the thread?
I have my Main Form:
using System;
using System.Windows.Forms;
//https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.control.invoke?view=net-5.0#System_Windows_Forms_Control_Invoke_System_Delegate_System_Object___
namespace YieldMonitor
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private void MainForm_Load(object sender, EventArgs e)
{
}
private void BtnConnectBT_Click(object sender, EventArgs e)
{
//Start looking for the yield monitor device.
BluetoothScan bluetoothScan = new BluetoothScan(this);
bluetoothScan.BluetoothDeviceDiscovered += OnBluetoothDeviceDiscovered;
}
static void OnBluetoothDeviceDiscovered(object sender, EventArgs e)
{
System.Diagnostics.Debug.WriteLine("Message recieved from event");
}
}
}
My class that looks for bluetooth devices and if the right one is found should fire the event:
using InTheHand.Net.Sockets;
using System;
using System.Linq;
namespace YieldMonitor
{
class BluetoothScan
{
public event EventHandler BluetoothDeviceDiscovered;
public BluetoothScan(MainForm mainForm)
{
System.Diagnostics.Debug.WriteLine("Starting BluetoothScan Class");
Run();
}
public void Run()
{
System.Diagnostics.Debug.WriteLine("Running BluetoothScan Class");
string myDeviceName;
ulong myDeviceAddress;
BluetoothClient btClient = new BluetoothClient();
BluetoothDeviceInfo[] btDevices = btClient.DiscoverDevices().ToArray();
foreach (BluetoothDeviceInfo d in btDevices)
{
System.Diagnostics.Debug.WriteLine(d.DeviceName);
System.Diagnostics.Debug.WriteLine(d.DeviceAddress);
//have we found the device we are looking for?
if (d.DeviceName == "DSD TECH HC-05")
{
myDeviceName = d.DeviceName;
myDeviceAddress = d.DeviceAddress;
//Send out found adapter to the next stage
OnBluetoothScanned(EventArgs.Empty);
break;
}
}
}
protected virtual void OnBluetoothScanned(EventArgs e)
{
System.Diagnostics.Debug.WriteLine("Running OnBlueToothScanned");
EventHandler handler = BluetoothDeviceDiscovered;
if (handler != null)// we have a subscriber to our event
{
System.Diagnostics.Debug.WriteLine("BluetoothScanned is Not empty");
handler(this, e);
}
else
{
System.Diagnostics.Debug.WriteLine("BluetoothScanned is Empty");
}
}
}
}
EDIT
I've found some nice solutions using Tasks where I need to update a label once a task is completed ie.
bool myDevicePaired = false;
var eventDevicePaired = new Progress<bool>(boDevicePaired => myDevicePaired = boDevicePaired);
await Task.Factory.StartNew(() => BluetoothPair.Run(myDeviceAddress, eventDevicePaired), TaskCreationOptions.LongRunning);
//Register the device is paired with the UI
if (myDevicePaired)
{
BtnConnectBT.Text = "Disconnect?";
}
Which is working well for Tasks that have an end that I am waiting for example waiting for a bluetooth device to connect.
But I'm beginning to pull my hair out with System.InvalidOperationException: 'Cross-thread operation not valid: Control 'tbInfo' accessed from a thread other than the thread it was created on.' error when trying to update a form text box.
Example:
in my MainForm Class:
I create what I've called an Event Reciever...
private void BluetoothSocketEventReciever(object sender, EventArgs e)
{
Debug.WriteLine("Event!!!"); //writes data to debug fine
tbInfo.AppendText("Event!!!!"); //causing error
}
I create a task to read from the device...
private void ReadDataFromDevice(UInt64 myDeviceAddress)
{
BluetoothSocket bluetoothSocket = new BluetoothSocket(myDeviceAddress);
bluetoothSocket.BluetoothDataRecieved += BluetoothSocketEventReciever;
Task.Factory.StartNew(() => bluetoothSocket.Run(), TaskCreationOptions.LongRunning);
}
In my BluetoothSocket class I have an endless while loop which will be reading data from a socket (hopefully) At the moment its just creating an empty EventArgs to trigger the Event every second:
namespace YieldMonitor
{
class BluetoothSocket
{
ulong myDeviceAddress;
public event EventHandler BluetoothDataRecieved;
public BluetoothSocket (ulong deviceAddress)
{
myDeviceAddress = deviceAddress;
}
public void Run()
{
System.Diagnostics.Debug.WriteLine("Were in BluetoothSocket ... Address: " + myDeviceAddress);
while (true)
{
Thread.Sleep(1000);
Debug.WriteLine("In BluetoothSocket - Address = " + myDeviceAddress);
OnBluetoothDataRecieved(EventArgs.Empty);
}
}
protected virtual void OnBluetoothDataRecieved(EventArgs e)
{
EventHandler handler = BluetoothDataRecieved;
if (handler != null)
{
handler(this, e);
} else
{
//No subscribers
}
}
}
}
I'm sure I'm missing something simple here but how can I pass the data from the endless loop to the text box on the main form?
EDIT
Think I've just sorted it.
private void BluetoothSocketEventReciever(object sender, EventArgs e)
{
Debug.WriteLine("Event!!!");
tbInfo.Invoke((Action)delegate
{
tbInfo.AppendText("Event!!!");
});
//tbInfo.AppendText("Event!!!!");
}
Is this the correct way to do it?
You can Pass the event handler as a parameter on the constructor
public event EventHandler BluetoothDeviceDiscovered;
public BluetoothScan(MainForm mainForm, EventHandler bluetoothDeviceDiscovered)
{
System.Diagnostics.Debug.WriteLine("Starting BluetoothScan Class");
BluetoothDeviceDiscovered += bluetoothDeviceDiscovered
Run();
}
Personally, i'm not so fun of calling method on constructor. It can be source of bugs or performance issues
Constructor
In class-based object-oriented programming, a constructor
(abbreviation: ctor) is a special type of subroutine called to create
an object. It prepares the new object for use, often accepting
arguments that the constructor uses to set required member variables.
You can pass eventhandler as parameter and call Run later

Asynchronous method using delegates [duplicate]

This question already has an answer here:
async Task vs async void
(1 answer)
Closed 3 years ago.
How would I make the main thread wait until DisplayAdd has displayed the output? If I add a Console.Read() at the end, everything works but is there another way to tell the main thread to wait until Calculate() has finished?
namespace TestDelegate
{
public class Add
{
public delegate void SendResult(int i);
public SendResult WhereToSend;
public async void Calculate (int number)
{
Console.WriteLine("Entered");
int result = number + number;
await Task.Delay(4000);
WhereToSend (result);
// Console.Read();
}
}
}
namespace TestStuff
{
class Program
{
static void Main(string[] args)
{
Add obj = new Add();
Console.WriteLine("Started Calculating");
obj.Calculate(10);
obj.WhereToSend = DisplayAdd;
}
static void DisplayAdd(int value)
{
Console.WriteLine(value);
}
}
}
You can define the delegate as Task return type (awaitable type). With this the method will finish before main thread terminates.
namespace TestDelegate
{
public delegate Task SendResult(int i);
public class Add
{
public SendResult WhereToSend;
public async Task Calculate (int number)
{
Console.WriteLine("Entered");
int result = number + number;
await WhereToSend (result);
}
}
}
namespace TestStuff
{
class Program
{
static void Main(string[] args)
{
Add obj = new Add();
obj.WhereToSend = DisplayAdd;
Console.WriteLine("Started Calculating");
obj.Calculate(10).Wait();
}
static async Task DisplayAdd(int value)
{
// Some awaitable operation like below as per your business logic
await Task.Delay(1);
Console.WriteLine(value);
}
}
}
In above program, I've changed the definition of Calculate method to async Task so that it can be marked for Waitable. The async void method are primarily used for UI events hanlder or fire and forget method.
Please check this dotnetfiddle which demonstrates the scenario.

Suggested pattern for gracefully restart cancelled async Tasks on non UI application

I have a console app that instantiates a WeatherClientManager class.
The main thread in the console app requests current weather status in the WeatherClientManager class, but the WeatherClientManager class continuously receives data from a server.
In code:
public static void Main(string [])
{
Program p = new Program();
Task.Run(()=>p.RunLoop());
}
class Program{
WeatherClientManager wcM;
public void RunLoop()
{
wcM = new WeatherClientManager ();
await wcM.InitiateConnection().ConfigureAwait(false);
}
}
class WeatherClientManager
{
public async Task<bool> InitiateConnection()
{
TCPClient tcpClient = new TcpClient(GetTCPDetailsFromConfig())
await tcpClient.ConnectAsync()
CancellationTokenSource cts = new CancellationTokenSource();
if(tcpClient.Connected)
{
Task.Run(()=>ReceiveTask(cts.Token));
Task.Run(()=>SendKeepAlive(cts.Token));
return true;
}
return false;
}
private void ReceiveTask(CancellationToken t)
{
try{
networkStream.Receive(..) // throws exception
}
catch(Exception e)
{
Stop(e);
}
}
private void SendKeepAlive(CancellationToken t)
{
while(!t.IsCancellationRequested)
{
try{
networkStream.Write(..) // throws exception
}
catch(Exception e)
{
Stop(e);
}
}
}
private void Stop(Exception e )
{
log.Error(e);
e.Cancel();
}
}
One of many crap ideas I have is:
Task.Run( () =>
{
while(true)
{
var t1 = Task.Run(()=>ReceiveTask(cts.Token));
var t2= Task.Run(()=>SendKeepAlive(cts.Token));
try{
Tasks.WhenAny(); // should block
}
catch(Exception e)
{
}
finally{
Cleanup();
InitiateConnections();
}
}
}
But I hate the idea of spinning a task to control two sub tasks. My problem is where and how to re-initiate the connection. Any ideas?
EDIT:
I've updated the code such that WeatherClientManager has a OnDisconnectDetected event. So the Program.cs class subscribes like so:
weatherServerManager.OnDisconnectDetected += HandleDisconnectDetection
public async void HandleDisconnectDetection()
{
wsM = new WeatherClientManager ();
wsM.InitiateConnection().ConfigureAwait(false);
}
private void SendKeepAlive(CancellationToken t)
{
while (...)
{
try{}
catch(Exception e)
{
OnDisconnectDetected?.Invoke();
}
}
}
When the handler is invoked by the WeatherClientManager it creates a new task that should continue in a different context. The KeepAlive task should exit then.
Still feels hacky but ideas welcome!
As a general rule, I prefer composition of methods over raising events. In particular, avoid the Task.Run-based fire-and-forget.
In the case of asynchronous sockets, I think it makes sense to give each socket a main loop:
class WeatherClientManager
{
public async Task MainLoop()
{
TCPClient tcpClient = new TcpClient(GetTCPDetailsFromConfig())
await tcpClient.ConnectAsync();
CancellationTokenSource cts = new CancellationTokenSource();
var receiveTask = Task.Run(()=>ReceiveTask(cts.Token));
var keepaliveTask = Task.Run(()=>SendKeepAlive(cts.Token));
await Task.WhenAll(receiveTask, keepaliveTask);
}
}
These can then be composed into the main program's main loop:
class Program
{
public async Task RunLoop()
{
while (true)
{
wcM = new WeatherClientManager();
await wcM.MainLoop();
}
}
}
which in turn is composed into Main:
public static void Main(string [])
{
Program p = new Program();
p.RunLoop().GetAwaiter().GetResult();
}
By avoiding fire-and-forget, you're ensuring that your code will always observe all exceptions. Ignoring tasks is occasionally okay but usually a mistake.

How to wait for thread to complete without blocking UI

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.

Properly exiting a .net console application that uses .NET 4.0 Tasks

I have a console application that essentially looks like this
class Program
{
static void Main(string[] args)
{
DoStuffService svc = new DoStuffService();
svc.Start();
}
}
class DoStuffService
{
public void Start()
{
Task.Factory.StartNew(() => { LongRunningOperation() });
}
private void LongRunningOperation()
{
// stuff
}
}
What's the best way these days to ensure my console application doesn't exit before LongRunningOperation() is complete, and also allows me a way to be notified in the console application (for logging purposes for instance) that LongRunningOperation() is complete.
call Wait() on the task. For example:
class Program
{
static void Main(string[] args)
{
DoStuffService svc = new DoStuffService();
svc.Start();
// stuff...
svc.DelayTilDone();
}
}
public class DoStuffService
{
Task _t;
public void Start()
{
_t = Task.Factory.StartNew(() => { LongRunningOperation(); });
}
public void DelayTilDone()
{
if (_t==null) return;
_t.Wait();
}
private void LongRunningOperation()
{
System.Threading.Thread.Sleep(6000);
System.Console.WriteLine("LRO done");
}
}
In addition to Cheeso's answer, you'll want to handle Console.CancelKeyPress so that you can display a busy message and set e.Cancel = True.
There's nothing you can do to prevent them from killing the process, but you can at least handle Ctrl+C and Ctrl+Break.
There is a similar thread C# multi-threaded console application - Console quits before threads complete
You can simply return a started task and Wait() or ContinueWith() on it:
using System.Diagnostics;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
DoStuffService svc = new DoStuffService();
svc.Start().Wait();//bool res = svc.Start()
Trace.WriteLine("333333333333333");
}
}
public class DoStuffService
{
public Task Start()
{
return Task.Factory.StartNew
(() =>
{
Trace.WriteLine("111111111");
LongRunningOperation(); ;
});
}
private void LongRunningOperation()
{
System.Threading.Thread.Sleep(3000);
Trace.WriteLine("2222222222");
}
}
A task will block the parent thread until completion, if to access its Result property, so:
using System.Diagnostics;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
DoStuffService svc = new DoStuffService();
svc.Start();//bool res = svc.Start()
Trace.WriteLine("333333333333333");
}
}
public class DoStuffService
{
public Task<bool> MyTask;
public bool Start()
{
MyTask = Task.Factory.StartNew<bool>
(() =>
{
Trace.WriteLine("111111111");
return LongRunningOperation();;
});
return MyTask.Result;
}
private bool LongRunningOperation()
{
System.Threading.Thread.Sleep(3000);
Trace.WriteLine("2222222222");
return true;
}
}

Categories

Resources