I am using 3d party library.
And i have the following code:
Session s = new Session(AuthParam.Login, AuthParam.Password);
s.Connect();
s.Connected += (sender, eventArgs) =>
{
_contactCollection = s.ContactList.Contacts.Select(x => new Contact(x.Nickname, x.Uin)).ToList();
};
s.ConnectionError += (sender, eventArgs) =>
{
};
s.Dispose();
s.Connect working in separate thread. So i want stop executing function. Wait while raised events and then continie executing. How can i do it?
Why could you not reorder your code such that the event handlers are added before calling Connect?
Session s = new Session(AuthParam.Login, AuthParam.Password);
s.Connected += (sender, eventArgs) =>
{
_contactCollection = s.ContactList.Contacts.Select(x => new Contact(x.Nickname, x.Uin)).ToList();
};
s.ConnectionError += (sender, eventArgs) =>
{
};
s.Connect();
This way you are guarenteed to get the raised events caused by the Connect method since they are wired in before Connect is called.
There is really no way to prevent Connect from executing once it is called.1
1I suppose you could execute Connect on a separate thread and then suspend that thread, but that is fraught with problems; too many to enumerate here.
Related
I have WPF program which listens what income data from Pushbullet through websocket (onMessage event)
The process of data is done in a background worker (in DoWork event) in order to not freeze the UI.
But might happen the data are more than one and seems that results are broken.
WS.onMessage += (s, ev) => {
var wk = new BackgroundWorker();
wk.DoWork += (se, evt) => {
Process1();
Dispatcher.Invoke(()=>{ print to ui });
}
wk.RunWorkerAsync();
}
How to process item one by one when onMessage is triggered? I mean I want to process an item and the process another after first is done even onMessage is already triggered few times, depends how many data comes ...
I'd suggest looking at Microsoft's Reactive Framework (NuGet "System.Reactive"). Then you can do this:
IDisposable subscription =
Observable
.FromEventPattern<EventHandler, EventArgs>(
h => WS.onMessage += h, h => WS.onMessage -= h)
.SelectMany(ep => Observable.Start(() => Process1()))
.ObserveOnDispatcher()
.Subscribe(x =>
{
/* Print to UI */
});
It'll process on a background thread and then marshall back to the dispatcher. It'll produce zero or more values in the .Subscribe method and will only produce one value at a time.
If you want to close down the observable just call .Dispose() on the subscription.
You might need to adjust the EventHandler & EventArgs types to suit your source event.
I am using Websockets to draw data on my canvas:
webSocket.OnMessage += (sender, e) =>
{
String websocketData = e.Data.Substring(3);
WebSocketDataStructure jsonData = JsonConvert.DeserializeObject<WebSocketDataStructure>(websocketData);
// Give control back to main thread for drawing
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(() => this.updateCanvas(jsonData)));
};
private void updateCanvas(WebSocketDataStructure myData)
{
CanvasUtils.DrawLine(MyCanvas, colorNormalBrush, myData.hsLine.x1, myData.hsLine.y1, myData.hsLine.x2, myData.hsLine.y2);
}
When I get multiple messages per second the application starts to lag. I read that using Dispatcher.BeginInvoke() is bad for handling frequent data, since we immediately switch back to the UI-Thread every time.
Is there a better way to implement this? I thought about creating a timer and updating the UI-Thread every full second. This would work by storing websocket data in a List, and process it on the UI-Thread (https://en.wikipedia.org/wiki/Producer%E2%80%93consumer_problem). My only problem with this approach was, that I couldn't set up an endless loop with Thread.Sleep(1000) on the UI-Thread.
You could queue your high-frequent data and read items from the data queue at a lower pace. By using a DispatcherTimer you could avoid the need for directly calling the Dispatcher.
var queue = new ConcurrentQueue<WebSocketDataStructure>();
webSocket.OnMessage += (s, e) =>
{
var websocketData = e.Data.Substring(3);
var jsonData = JsonConvert.DeserializeObject<WebSocketDataStructure>(websocketData);
queue.Enqueue(jsonData);
};
var timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };
timer.Tick += (s, e) =>
{
WebSocketDataStructure data;
while (queue.TryDequeue(out data))
{
CanvasUtils.DrawLine(MyCanvas, colorNormalBrush,
data.hsLine.x1, data.hsLine.y1, data.hsLine.x2, data.hsLine.y2);
}
};
timer.Start();
I see people write code like this
_Worker = new BackgroundWorker();
_Worker.DoWork += (sender, e) => e.Result = ((Func<string>)e.Argument)();
Why people assign e.Result if they don't use it. There is no code below that uses this assignment.
But! They write below a piece of code where use RunWorkerCompleted event. Does RunWorkerCompleted event use e automatically when rises (precisely this e above that I assigned)?
You are right. The e.result can be used in the RunWorkerCompleted Event. Because DoWork Runs in a own Thread that's the way how you can reuse your results to main thread. Here you can read more about it.
Notice that backgroundworker is a bit oldschool and read about Task Library if you are interested in modern C# Multithreading purposes ( >=.Net 3.5)
Small Example:
_Worker = new BackgroundWorker();
_Worker.DoWork += (sender, e) => e.Result = ((Func<string>)e.Argument)();
_Worker.RunWorkerCompleted += (sender,e) => textbox.Text = e.Result.ToString();
I have a backgroundworker that has been created using a lambda as shown here:
BackgroundWorker fileCountWorker= new BackgroundWorker();
fileCountWorker.WorkerSupportsCancellation = true;
fileCountWorker.DoWork += new DoWorkEventHandler((obj, e) => GetFileInfo(folder, subs));
fileCountWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler((obj, e) => UpdateCountInFolderListViewForItem(index));
fileCountWorker.RunWorkerAsync();
I would like to be able to cancel the backgroundworker, and then know that it was canceled in the RunWorkerCompleted function using the RunWorkerCompletedEventArgs e.Canceled property.
So far I have been unable to figure out a way to pass a parameter to the RunWorkerCompleted function and still maintain the ability to access the RunWorkerCompletedEventArgs.
I tried adding a RunWorkerCompletedEventArgs parameter to the function called by RunWorkerCompleted, and then passing the RunWorkerCompletedEventArgs like so:
fileCountWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler((obj, e) => UpdateCountInFolderListViewForItem(index, e));
But that didn't seem to work.
Is there a way to do this?
Edit:
Following comments below, I made the following changes:
I changed the DoWork Event as follows (adding the obj and e as parameters in the worker function):
fileCountWorker.DoWork += new DoWorkEventHandler((obj, e) => GetFileInfo(folder, subs,obj,e));
I then changed the RunWorkerCompleted function as follows (adding the obj and e as parameters in the RunWorkerCompleted function):
fileCountWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler((obj, e) => UpdateCountInFolderListViewForItem(index, obj, e));
From my UI Thread I call CancelAsync:
if (bgw.WorkerSupportsCancellation)
{
bgw.CancelAsync();
}
Then from within the backgroundworker I check for cancellationpending like:
BackgroundWorker bwAsync = sender as BackgroundWorker;
if (bwAsync.CancellationPending)
{
e.Cancel = true;
return;
}
The result is that when I cancel the backgroundworker, it does stop the worker function, but theRunWorkerCompletedEventArgs in the RunWorkerCompleted function ( UpdateCountInFolderListViewForItem) still has a Canceled property set to False, so the function can't tell that the worker was canceled.
So i'm still stuck on getting the RunWorkerCompleted function to know that the worker was canceled instead of completing normally.
You just need to call BackgroundWorker.CancelAsync().
Your worker code needs to check BackgroundWorker.CancellationPending and stop what it's doing to "cancel"... But, your lambda isn't doing anything you can really cancel.
Normally what you'd do is something like this:
//...
fileCountWorker.DoWork += (obj, e) =>
{
for (int i = 0; i < 1000 && fileCountWorker.CancellationPending; ++i)
{
Thread.Sleep(500);/* really do other work here */
}
e.Cancel = fileCountWorker.CancellationPending;
};
fileCountWorker.RunWorkerAsync();
//...
fileCountWorker.CancelAsync();
If you provide some details of GetFileInfo, maybe some more detail could be provided.
I'm using several external services when items in my application are completed. Each service I must contact gets it's own BackgroundWorker to perform its task. I keep track of how many workers completed their work to determine if there were any errors. The issue I'm running into is that RunWorkerCompleted doesn't always get fired.
Here is what I have:
var exceptionsCaught = new List<Exception>();
var completedCount = 0;
foreach(var notificationToSend in NotificationQueue)
{
var backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += (sender, b) => notificationToSend();
backgroundWorker.RunWorkerCompleted += (sender, args) =>
{
Console.WriteLine("Done.");
if(args.Error != null)
{
exceptionsCaught.Add(args.Error);
Console.WriteLine("Error! {0}", args.Error.Message);
}
completedCount++;
};
backgroundWorker.RunWorkerAsync();
}
var exceptionCheckerWorker = new BackgroundWorker();
exceptionCheckerWorker.DoWork += (sender, b) =>
{
while (true)
{
if (completedCount != NotificationQueue.Count) continue;
if (exceptionsCaught.Any())
{
var notificationExceptionMessage =
new NotificationExceptionsMessage(exceptionsCaught,
_notificationGeneratedPath);
_eventAggregator.Publish(notificationExceptionMessage);
break;
}
break;
}
};
exceptionCheckerWorker.RunWorkerAsync();
What am I missing?
When it fails to get called I see The thread 0x2e0 has exited with code 0 (0x0). in my console.
I have changed the way I increment my count by using Interlocked but that hasn't changed anything. I know the event isn't being called because I have a breakpoint set.
Any ideas?
Are you sure that the RunWorkerCompleted event is not raised? The most likely cause is that you're not incrementing the completedCount field properly. Try using the Interlocked methods:
Interlocked.Increment(ref completedCount);
You have race condition when incrementing completedCount. You should use Interlocked.Increment
I don't know but you are writing a multi-threaded procedure that shears global data without a single lock, something surly is going wrong.