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
Related
Gist of it has probably been asked before, but I'm completely lost so I'm looking for some personal guidance. Been trying to make a stock tracker app for funsies using WinForms and the Yahoo API. Trying to get it so you can input a tracker symbol and it will make a new Label that will keep updating itself every so often. However, it keeps giving me error messages about "Cross-thread operation not valid". I've tried to do some googling, but yeah, completely lost. Here is most of the code, hope you guys can make some sense of it.
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using YahooFinanceApi;
namespace stockpoging4
{
public partial class Form1 : Form
{
public Form1()
{
System.Globalization.CultureInfo.DefaultThreadCurrentUICulture = System.Globalization.CultureInfo.GetCultureInfo("en-US");
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
using (Prompt prompt = new Prompt("Enter the ticker symbol", "Add ticker"))
{
string result = prompt.Result;
result = result.ToUpper();
if (!string.IsNullOrEmpty(result))
{
do_Things(result);
}
}
}
public async Task<string> getStockPrices(string symbol)
{
try
{
var securities = await Yahoo.Symbols(symbol).Fields(Field.RegularMarketPrice).QueryAsync();
var aapl = securities[symbol];
var price = aapl[Field.RegularMarketPrice];
return symbol + " $" + price;
}
catch
{
return "404";
}
}
public async void do_Things(string result)
{
string price;
Label label = null;
if (label == null)
{
price = await getStockPrices(result);
label = new Label() { Name = result, Text = result + " $" + price };
flowLayoutPanel2.Controls.Add(label);
}
else
{
Thread testThread = new Thread(async delegate ()
{
uiLockingTask();
price = await getStockPrices(result);
label.Text = result + " $" + price;
label.Update();
});
}
System.Timers.Timer timer = new System.Timers.Timer(10000);
timer.Start();
timer.Elapsed += do_Things(results);
}
private void uiLockingTask() {
Thread.Sleep(5000);
}
}
}
Let me point out several things in your implementation.
You subscribe to timer.Elapsed after timer.Start that might be invalid in case of a short-timer interval
The event handler is called in background that's why you continuously get "Cross-thread operation not valid". UI components should be dispatched correctly from background threads, for example, by calling flowLayoutPanel2.BeginInvoke(new Action(() => flowLayoutPanel2.Controls.Add(label))); and label.BeginInvoke(new Action(label.Update)). This change already would fix your exception.
Despite the fact that I would implement this functionality in a different way, here I post slightly changed code that just does exactly what you need with some tweaks.
public partial class Form1 : Form
{
Task _runningTask;
CancellationTokenSource _cancellationToken;
public Form1()
{
System.Globalization.CultureInfo.DefaultThreadCurrentUICulture = System.Globalization.CultureInfo.GetCultureInfo("en-US");
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
using (Prompt prompt = new Prompt("Enter the ticker symbol", "Add ticker"))
{
string result = prompt.Result;
result = result.ToUpper();
if (!string.IsNullOrEmpty(result))
{
do_Things(result);
_cancellationToken = new CancellationTokenSource();
_runningTask = StartTimer(() => do_Things(result), _cancellationToken);
}
}
}
private void onCancelClick()
{
_cancellationToken.Cancel();
}
public async Task<string> getStockPrices(string symbol)
{
try
{
var securities = await Yahoo.Symbols(symbol).Fields(Field.RegularMarketPrice).QueryAsync();
var aapl = securities[symbol];
var price = aapl[Field.RegularMarketPrice];
return symbol + " $" + price;
}
catch
{
return "404";
}
}
private async Task StartTimer(Action action, CancellationTokenSource cancellationTokenSource)
{
try
{
while (!cancellationTokenSource.IsCancellationRequested)
{
await Task.Delay(1000, cancellationTokenSource.Token);
action();
}
}
catch (OperationCanceledException) { }
}
public async void do_Things(string result)
{
var price = await getStockPrices(result);
var label = new Label() { Name = result, Text = result + " $" + price };
flowLayoutPanel2.BeginInvoke(new Action(() => flowLayoutPanel2.Controls.Add(label)));
}
}
A much easier way is using async these days.
Here is a class which triggers an Action every interval:
public class UITimer : IDisposable
{
private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
// use a private function which returns a task
private async Task Innerloop(TimeSpan interval, Action<UITimer> action)
{
try
{
while (!_cancellationTokenSource.IsCancellationRequested)
{
await Task.Delay(interval, _cancellationTokenSource.Token);
action(this);
}
}
catch (OperationCanceledException) { }
}
// the constructor calls the private StartTimer, (the first part will run synchroniously, until the away delay)
public UITimer(TimeSpan interval, Action<UITimer> action) =>
_ = Innerloop(interval, action);
// make sure the while loop will stop.
public void Dispose() =>
_cancellationTokenSource?.Cancel();
}
If you work with dotnet 3.0 or higher, you can use the IAsyncDisposable. With this you're able to await the DisposeAsync method, so you can await the _timerTask to be finished.
And I created a new form with this as code behind:
public partial class Form1 : Form
{
private readonly UITimer _uiTimer;
private int _counter;
public Form1()
{
InitializeComponent();
// setup the time and pass the callback action
_uiTimer = new UITimer(TimeSpan.FromSeconds(1), Update);
}
// the orgin timer is passed as parameter.
private void Update(UITimer timer)
{
// do your thing on the UI thread.
_counter++;
label1.Text= _counter.ToString();
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
// make sure the time (whileloop) is stopped.
_uiTimer.Dispose();
}
}
The advantage is, that the callback runs on the UI thread but doesn't block it. The await Task.Delay(..) is using a Timer in the background, but posts the rest of the method/statemachine on the UI thread (because the UI thread has a SynchronizaionContext)
Easy but does the trick ;-)
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 */ }
}
Every time when timer invoke UpdateDocumentsListFromServer UI freezes for 3 seconds. How to update list in async style under .net 3.5?
ViewModel:
public class ShippingDocumentsRegisterViewModel : ViewModelBase
{
ShippingDocumentsModel model = new ShippingDocumentsModel();
DispatcherTimer timer = new DispatcherTimer();
BackgroundWorker BW = new BackgroundWorker();
public ShippingDocumentsRegisterViewModel()
{
timer = new DispatcherTimer();
timer.Tick += new EventHandler(UpdateDocumentsListFromServer);
timer.Interval = new TimeSpan(0, 0, 10);
timer.Start();
this.Columns = model.InitializeColumns();
BW.DoWork += UpdateDocumentsList;
BW.RunWorkerAsync();
}
public void UpdateDocumentsList(object o, EventArgs args)
{
this.ShippingDocuments = model.GetDocuments();
}
public void UpdateDocumentsListFromServer(object o, EventArgs args)
{
// Taking a lot of time. How to do it async?
var tempDocuments = model.GetDocumentsFromServer();
foreach (var item in tempDocuments)
{
this.shippingDocuments.Add(item);
}
//
}
private ObservableCollection<ShippingDocument> shippingDocuments;
public ObservableCollection<ShippingDocument> ShippingDocuments
{
get
{
return shippingDocuments;
}
private set
{
shippingDocuments = value;
RaisePropertyChanged("ShippingDocuments");
}
}
public ObservableCollection<ShippingDocumentColumDescriptor> Columns { get; private set; }
}
GetDocumentsFromServer look like
public ObservableCollection<ShippingDocument> GetDocumentsFromServer()
{
System.Threading.Thread.Sleep(3000);
return new ObservableCollection<ShippingDocument> { new ShippingDocument { Name = "Test" } };
}
You could also use a background worker that reports progress to the UI
public ShippingDocumentsRegisterViewModel()
{
BW.DoWork += UpdateDocumentsListFromServer;
BW.RunWorkerCompleted += BW_RunWorkerCompleted;
BW.WorkerReportsProgress = true;
BW.ProgressChanged += UpdateGui;
BW.RunWorkerAsync();
}
public void UpdateGui(object o, EventArgs args)
{
foreach (var item in tempDocuments)
{
this.shippingDocuments.Add(item);
}
}
public void UpdateDocumentsListFromServer(object o, EventArgs args)
{
while (true) {
System.Threading.Thread.Sleep(3000);
tempDocuments = GetDocumentsFromServer();
BW.ReportProgress(0);
}
}
int num = 0;
public ShippingDocument[] GetDocumentsFromServer()
{
System.Threading.Thread.Sleep(3000);
return new ShippingDocument[1] { new ShippingDocument { Name = "Test" + num++} };
}
private ShippingDocument[] tempDocuments = new ShippingDocument[0];
Just offload it to a new thread using Task and Async/Await like so:
public async void UpdateDocumentsListFromServer(object o, EventArgs args)
{
// This will execute async and return when complete
await Task.Run(()=>{
var tempDocuments = model.GetDocumentsFromServer();
foreach (var item in tempDocuments)
{
this.shippingDocuments.Add(item);
}
});
//
}
Keep in mind this updates on a different thread then the UI. So it is not allowed to touch anything on the UI thread or you will get threading issues. So if shippingDocuments was created on the UI thread and is not thread-safe you could instead return a collection of items then add them:
public async void UpdateDocumentsListFromServer(object o, EventArgs args)
{
// Execute on background thread and put results into items
var items = await Task.Run(()=>{
var tempDocuments = model.GetDocumentsFromServer();
return tempDocuments;
});
//add occurs on UI thread.
this.shippingDocuments.AddRange(tempDocuments);
}
Use a regular Timer and only dispatch the access to shippingDocuments.
As mentioned in comment, you can make use of Timers instead of DispatcherTimer. DispactherTimer will access the UIThread where as Timer use different thread from threadpool.
Also, you can dispatch an action to UIThread from different thread
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
//Do some UI stuffs
}));
Hope that helps.
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();
}
}
I have a function that is called in rapid succession that has a open database connection.
my issue is that before one database connection is closed, another instance of the function is called and i could possibly receive a deadlock in the database.
I have tried:
private static WaitHandle[] waitHandles = new WaitHandle[]
{
new AutoResetEvent(false)
};
protected override void Broadcast(Data data, string updatedBy)
{
Action newAction = new Action(() =>
{
DataManagerFactory.PerformWithDataManager(
dataManager =>
{
// Update status and broadcast the changes
data.UpdateModifiedColumns(dataManager, updatedBy);
BroadcastManager.Instance().PerformBroadcast(
data,
BroadcastAction.Update,
Feature.None);
},
e => m_log.Error(ServerLog.ConfigIdlingRequestHandler_UpdateFailed() + e.Message));
}
);
Thread workerThread = new Thread(new ThreadStart(newAction));
ThreadPool.QueueUserWorkItem(workerThread.Start, waitHandles[0]);
WaitHandle.WaitAll(waitHandles);
}
but i recieve a thread error and the program freezes. It has something to do with the thread start function having no parameters i believe.
Thanks for any help
This is how it's done. Create class that does the job:
public class MyAsyncClass
{
public delegate void NotifyComplete(string message);
public event NotifyComplete NotifyCompleteEvent;
//Starts async thread...
public void Start()
{
System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(DoSomeJob));
t.Start();
}
void DoSomeJob()
{
//just wait 5 sec for nothing special...
System.Threading.Thread.Sleep(5000);
if (NotifyCompleteEvent != null)
{
NotifyCompleteEvent("My job is completed!");
}
}
}
Now this is code from another class, that calls first one:
MyAsyncClass myClass = null;
private void button2_Click(object sender, EventArgs e)
{
myClass = new MyAsyncClass();
myClass.NotifyCompleteEvent += new MyAsyncClass.NotifyComplete(myClass_NotifyCompleteEvent);
//here I start the job inside working class...
myClass.Start();
}
//here my class is notified from working class when job is completed...
delegate void myClassDelegate(string message);
void myClass_NotifyCompleteEvent(string message)
{
if (this.InvokeRequired)
{
Delegate d = new myClassDelegate(myClass_NotifyCompleteEvent);
this.Invoke(d, new object[] { message });
}
else
{
MessageBox.Show(message);
}
}
Let me know if I need to explain some details.
Alternative to this is BackgroudWorker: