I have a problem: Our application has grown so far and therefore there is an object with many many EventHandlers. The point here is that removing them takes far more time, than adding them. This can be tested very fast:
Create a class with a
public event EventHandler
Make a second time and register to the event in first class like 300'000 times.
Here comes the interesting point: Registring needs like 0.1 seconds, whereas deregistring needs 5 minutes and it's growing almost exponentially.
Does anyone have a solution for that? The only one I found is to solve it over a WeakEventHandler (with it's downsides), but maybe there is another possiblity.
Example:
public class ClassWithValueChangedEvent
{
public event EventHandler ValueChanged;
public ClassWithValueChangedEvent()
{
}
}
public class MainWindowViewModel
{
public MainWindowViewModel()
{
DateTime start = DateTime.Now;
var classInstance = new ClassWithValueChangedEvent();
for (int i = 0; i < 300000; i++)
{
classInstance.ValueChanged += ClassWithValueChangedOnValueChanged;
}
Debug.WriteLine($"Elapsed Time: {(DateTime.Now - start).TotalSeconds}");
start = DateTime.Now;
for (int i = 0; i < 300000; i++)
{
classInstance.ValueChanged -= ClassWithValueChangedOnValueChanged;
}
Debug.WriteLine($"Elapsed Time: {(DateTime.Now - start).TotalSeconds}");
If we need to remove all event handlers
What I would do is that I would create an Unset method inside the class that nullifies the ValueChanged event handler like bellow:
public class ClassWithValueChangedEvent
{
public event EventHandler ValueChanged;
public void Unset()
{
ValueChanged = null;
}
}
Then, when needed I would do:
classInstance.Unset();
This is almost instantaneous.
If we need to remove individual event handlers
The process bellow is much more complicated and I don't know if I would choose it if I was in such need. The idea is that we create a custom list and we invoke the handlers manually:
public class ClassWithValueChangedEvent
{
private List<EventHandler> eventHandlers = new List<EventHandler>();
public void SetEventHandler(EventHandler evt)
{
eventHandlers.Add(evt);
}
public void UnsetEventHandler(EventHandler evt)
{
eventHandlers.Remove(evt);
}
public void UnsetAll()
{
eventHandlers.Clear();
}
public void CallEventHandlers(object? sender, EventArgs e)
{
eventHandlers.All(p => { p(sender, e); return true; });
}
}
Then, the methods of this class can be called as:
classInstance.SetEventHandler(ClassWithValueChangedOnValueChanged);
classInstance.CallEventHandlers(null, null);
classInstance.UnsetEventHandler(ClassWithValueChangedOnValueChanged);
Removing 300000 event handlers takes around 9 seconds which is much faster than minutes. I guess also that there must be a much faster way to do something like that.
I solved this question in using the WeakEventManager<>.AddHandler() from microsoft, which does not immediately remove the event handlers. It did not matter in this case.
Related
I am creating DLL which contains loop of some data
how can i display progress bar
I tried to create new windows Form and displayed the same in for loop
but it ask me to close the form every time
People on SO are not here to write code for you - they are here to solve problems. Anyway, I am going to show you how you could do it, and then you can write your code based on what I provide.
First of all, a "DLL" is a Dynamic-link library. Therefore, you can attach it to any project you have (winform or unity 3d game and no, you will not do it but let's just say it could be used in both cases) so if you are already writing DLL's, make it usable in a lot of scenarios and provide the programmer with a lot possibilities for manipulation.
So, your task is divided in 2 parts here:
Calculate data
Inform the user of what stage of the calculation they are at now
For this task, we will use events and a simple for loop to show you how it works.
First of all, let's create an EventArgs class that will store all the data we want to pass when the programmer from the other code catches the event:
public class CustomEventArgs
{
public int OldResult { get; set; }
public int NewResult { get; set; }
}
Now, when we have an event class let's implement it in our code.
public class YourDllCalculation
{
// In the .NET Framework class library, events are based on the EventHandler delegate and the EventArgs base class.
// So we create delegate using our newly created class to represents it like EventHandler
public delegate void ResultChangeEventHandler(object sender, CustomEventArgs e);
// Now we create our event
public event IzborRobeEventHandler ResultChanged;
// Local storing variable
private int Result = 0;
// This is method from which you inform event something changed and user listening to event catch EventArgs passed (in our case our CustomEventArgs)
protected virtual void OnResultChange(CustomEventArgs e)
{
ResultChangeEventHandler h = ResultChanged;
if (h != null)
h(this, e);
}
// We will use this method from new code to start calculation;
public void StartCalculation()
{
// Calculation will be done in separate thread so your code could proceed further if needed
Thread t1 = new Thread(Calculate);
t1.Start();
}
private void Calculate()
{
for(int i = 0; i < 100; i++)
{
OnResultChange(new CustomEventArgs() { OldResult = i, NewResult = i + 1 });
Result = i;
Thread.Sleep(1000); // Pause thread from running for 1 sec
}
}
}
Now that we have our code we can use it like this in our Winform:
// Add at top
using YourDllNamespace;
public YourForm()
{
// Creating our class for calculation
YourDllCalculation calc = new YourDllCalculation();
calc += CalculationResultChanged;
calc.Calculate();
}
private void CalculationResultChanged(object sender, CustomEventArgs e)
{
// Here do whatever you want with passed values
// e.OldResult;
// e.NewResult;
// it will fire each second
}
during the last days I tracked down a memory leak in our application. I know the idea of the developer, who wrote this code, but I am not really about why the leak occurs. The idea was to have a class, which contains an event. The class will instantiate another class, which shall add a event handler to the event in the Init-method and removes it, calling the Stop-method. The problem is, that the event isn't removed from the main class and the InvocationTargetList is growing and growing.
Here is an example source code, which shows the problem:
public class SampleEventArgs
{
public SampleEventArgs(string s) { Text = s; }
public String Text { get; private set; }
}
public class MainClass
{
public delegate void SampleEventHandler(object sender, SampleEventArgs e);
public event SampleEventHandler SampleEvent;
public SubClass m_SubClass = new SubClass();
public MainClass()
{
for (int i = 0; i < 10000; i++)
{
m_SubClass.Init(ref SampleEvent);
m_SubClass.Close();
}
if (SampleEvent != null)
Console.WriteLine("SampleEvent InvocationTargetList length: {0}", SampleEvent.GetInvocationList().Length);
Console.ReadKey();
}
}
public class SubClass
{
public event MemoryLeakTest.MainClass.SampleEventHandler m_Subscription;
public void Init(ref MainClass.SampleEventHandler SampleEvent)
{
SampleEvent += NewEvent;
m_Subscription = SampleEvent;
}
public void Close()
{
m_Subscription -= NewEvent;
}
public void NewEvent(object sender, SampleEventArgs e)
{
}
}
Well, I already fixed the problem by passing the class to the Init and Stop method and also implemented the IDisposeable pattern, but I am not 100% sure, why the code above produces the memory leak. Is it caused, because assigning the SampleEvent to m_Suscription will create a copy of the event and so the event will only be removed from the m_Subscription variable in the Stop method? Any ideas?
Well, we already know delegates are immutable and that's why you used ref parameter to subscribe event.
Now look this method closely
public void Close()
{
m_Subscription -= NewEvent;
}
You actually unsubscribe from a copy, and not from original delegate which is in MainClass. In other words you're re-assigning the field in sub class only not the field in main class.
I want to send an alert message to any subscribers when a trap occurred.
The code I created works fine using a delegate method myDelegate del.
My questions are:
I want to know whether it's better to use EventHandler instead of a delegate?
I'm not sure what the differences are between a delegate and an EventHandler in my case.
notify(trapinfo t), that's what I've done here to get trap information. But it seems not to be a good idea. I read some online tutorial lesson introducing passing delegate object; I'm wondering if it's appropriate in my case? And how should I do it? Any suggestions?
Thanks a lot :)
My code:
public class trapinfo
{
public string info;
public string ip;
public string cause;
}
public class trap
{
public delegate void myDelegate(trapinfo t);
public myDelegate del;
trapinfo info = new trapinfo();
public void run()
{
//While(true)
// If a trap occurred, notify the subscriber
for (; ; )
{
Thread.Sleep(500);
foreach (myDelegate d in del.GetInvocationList())
{
info.cause = "Shut Down";
info.ip = "192.168.0.1";
info.info = "Test";
d.Invoke(info);
}
}
}
}
public class machine
{
private int _occuredtime=0;
public trapinfo info = new trapinfo();
public void notify(trapinfo t)
{
++_occuredtime;
info.cause = t.cause;
info.info = t.info;
info.ip = t.ip;
getInfo();
}
public void subscribe(trap t)
{
t.del += new trap.myDelegate(notify);
}
public void getInfo()
{
Console.WriteLine("<Alert>: cauese/{0}, info/ {1}, ip/{2}, time/{3}",
info.cause, info.info, info.ip,_occuredtime);
}
}
class Program
{
static void Main(string[] args)
{
trap t = new trap();
machine machineA = new machine();
machineA.subscribe(t);
t.run();
}
}
Update 2013-08-12
How about the observer/observable design pattern, that looks great in my case (EventHandler).
In my case, a machine subscribes to a trap messenger. (Add a machine to an invocation list)
Once a trap occurred, I send a message to all machines which are subscribed. (Call HandleEvent to handle it)
Advantages:
don't care about GetInvocationList() anymore, just use (+=) and (-=) to decide whom to send the trap.
It's easier to understand the logic of my program.
I know there are several ways to do it, but I wish I could analyze its pros and cons.
And thanks for your comments and suggestions, that would be very helpful!
I read the MSDN EventArgs article which Matthew Watson suggested.
Here's my Event Version:
public class TrapInfoEventArgs : EventArgs
{
public int info { get; set; }
public string ip { get; set; }
public string cause { get; set; }
}
public class trap
{
public event EventHandler<TrapInfoEventArgs> TrapOccurred;
protected virtual void OnTrapOccurred(TrapInfoEventArgs e)
{
EventHandler<TrapInfoEventArgs> handler = TrapOccurred;
if (handler != null)
{
handler(this, e);
}
}
public void run()
{
//While(true)
// If a trap occurred, notify the subscriber
for (; ; )
{
Thread.Sleep(500);
TrapInfoEventArgs args = new TrapInfoEventArgs();
args.cause = "Shut Down";
OnTrapOccurred(args);
}
}
}
public class machine
{
public void c_TrapOccurred(object sender, TrapInfoEventArgs e)
{
Console.WriteLine("<Alert>: cauese/{0}, info/ {1}, ip/{2}, time/{3}",
e.cause, e.info, e.ip, DateTime.Now.ToString());
}
}
class Program
{
static void Main(string[] args)
{
trap t = new trap();
machine machineA = new machine();
t.TrapOccurred += machineA.c_TrapOccurred; //notify machine A
t.run();
}
}
The difference between event and delegate is that:
event declaration adds a layer of protection on the delegate instance.
This protection prevents clients of the delegate from resetting the
delegate and its invocation list, and only allows adding or removing
targets from the invocation list
See What are the differences between delegates and events?
2) As I see it, your subscriber should not change delegates freely. One subscriber can assign = to it instead of adding +=. This will assign a new delegate, therefore, the previous delegate with its invocation list will be lost and previous subscribers will not be called anymore. So you should use Event for sure. Or you can change your code to make your delegate private and write additional functions for manipulating it to define your own event behavior.
//preventing direct assignment
private myDelegate del ;
public void AddCallback(myDelegate m){
del += m;
}
public void RemoveCallback(myDelegate m){
del -= m;
}
//or
public static trap operator +(trap x,myDelegate m){
x.AddCallback(m);
return x;
}
public static trap operator -(trap x, myDelegate m)
{
x.RemoveCallback(m);
return x;
}
//usage
//t.AddCallback(new trap.myDelegate(notify));
t+=new trap.myDelegate(notify);
It is much better to use an event for your example.
An event is understood by the Visual Studio Form and WPF designers, so you can use the IDE to subscribe to events.
When raising events, there is no need for you to write your own foreach handling to iterate through them.
events are the way that most programmers will expect this functionality to be accessed.
If you use a delegate, the consuming code can mess around with it in ways that you will want to prevent (such as resetting its invocation list). events do not allow that to happen.
As for your second question: Using an event you would create a class derived from EventArgs to hold the data, and pass that to the event when you raise it. The consumer will then have access to it.
See here for details: http://msdn.microsoft.com/en-us/library/system.eventargs.aspx
How would you suggest the best way of avoiding duplicate event subscriptions? if this line of code executes in two places, the event will get ran twice. I'm trying to avoid 3rd party events from subscribing twice.
theOBject.TheEvent += RunMyCode;
In my delegate setter, I can effectively run this ...
theOBject.TheEvent -= RunMyCode;
theOBject.TheEvent += RunMyCode;
but is that the best way?
I think, the most efficient way, is to make your event a property and add concurrency locks to it as in this Example:
private EventHandler _theEvent;
private object _eventLock = new object();
public event EventHandler TheEvent
{
add
{
lock (_eventLock)
{
_theEvent -= value;
_theEvent += value;
}
}
remove
{
lock (_eventLock)
{
_theEvent -= value;
}
}
}
I have done this before....it assumes it is acceptable that the last subscriber is what gets called.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
MyObject my = new MyObject();
my.Changed += new EventHandler(my_Changed);
my.Changed += new EventHandler(my_Changed1);
my.Update();
Console.ReadLine();
}
static void my_Changed(object sender, EventArgs e)
{
Console.WriteLine("Hello");
}
static void my_Changed1(object sender, EventArgs e)
{
Console.WriteLine("Hello1");
}
}
public class MyObject
{
public MyObject()
{
}
private EventHandler ChangedEventHandler;
public event EventHandler Changed
{
add
{
ChangedEventHandler = value;
}
remove
{
ChangedEventHandler -= value;
}
}
public void Update()
{
OnChanged();
}
private void OnChanged()
{
if (ChangedEventHandler != null)
{
ChangedEventHandler(this, null);
}
}
}
}
Is your code multi threaded ? Concurrency lock is needed only when its multi threaded. If not its a overhead.
As such your approach of unsubscribing and subscribing is correct.
Thanks
If you own the source for the class of theObject, then you have access to the InvocationList of TheEvent. You can implement your own add accessor for the event and check before adding.
However, I think that your approach is fine too.
I use your approach except one detail. I think, that events should be subscribed when you create new instance of subscriber or theObject, this makes code more straight. Thus, all you need is just carefully watch after correct objects disposing (dispose patten is convenient solution for this).
You mentioned that you use 3rd party event, that means that you can't provide your own realisation for add/remove methods, as you have been advised. But in your own classes with your own events you should define your own realisation of add/remove methods for event in order to solve your problem.
I find myself doing this sort of thing quite often:-
EventHandler eh = null; //can't assign lambda directly since it uses eh
eh = (s, args) =>
{
//small snippet of code here
((SomeType)s).SomeEvent -= eh;
}
variableOfSomeType.SomeEvent += eh;
Basically I only want to attach an event handler to listen for one shot from the event, I no longer want to stay attached after that. Quite often that "snippert of code" is just one line.
My mind is going a bit numb, I'm sure there must be something I can do so I don't need to repeat all this overhead. Bear in mind that EventHandler may well be EventHandler<T>.
Any ideas how I can tidy up the repeative part of the code and just leave the snippet in a Lambda?
You could attache a permanent event handler to the event. The event handler then invokes "one shot event handlers" that are added to an internal queue:
OneShotHandlerQueue<EventArgs> queue = new OneShotHandlerQueue<EventArgs>();
Test test = new Test();
// attach permanent event handler
test.Done += queue.Handle;
// add a "one shot" event handler
queue.Add((sender, e) => Console.WriteLine(e));
test.Start();
// add another "one shot" event handler
queue.Add((sender, e) => Console.WriteLine(e));
test.Start();
Code:
class OneShotHandlerQueue<TEventArgs> where TEventArgs : EventArgs {
private ConcurrentQueue<EventHandler<TEventArgs>> queue;
public OneShotHandlerQueue() {
this.queue = new ConcurrentQueue<EventHandler<TEventArgs>>();
}
public void Handle(object sender, TEventArgs e) {
EventHandler<TEventArgs> handler;
if (this.queue.TryDequeue(out handler) && (handler != null))
handler(sender, e);
}
public void Add(EventHandler<TEventArgs> handler) {
this.queue.Enqueue(handler);
}
}
Test class:
class Test {
public event EventHandler Done;
public void Start() {
this.OnDone(new EventArgs());
}
protected virtual void OnDone(EventArgs e) {
EventHandler handler = this.Done;
if (handler != null)
handler(this, e);
}
}
You can use reflection:
public static class Listener {
public static void ListenOnce(this object eventSource, string eventName, EventHandler handler) {
var eventInfo = eventSource.GetType().GetEvent(eventName);
EventHandler internalHandler = null;
internalHandler = (src, args) => {
eventInfo.RemoveEventHandler(eventSource, internalHandler);
handler(src, args);
};
eventInfo.AddEventHandler(eventSource, internalHandler);
}
public static void ListenOnce<TEventArgs>(this object eventSource, string eventName, EventHandler<TEventArgs> handler) where TEventArgs : EventArgs {
var eventInfo = eventSource.GetType().GetEvent(eventName);
EventHandler<TEventArgs> internalHandler = null;
internalHandler = (src, args) => {
eventInfo.RemoveEventHandler(eventSource, internalHandler);
handler(src, args);
};
eventInfo.AddEventHandler(eventSource, internalHandler);
}
}
Use it like so:
variableOfSomeType.ListenOnce("SomeEvent",
(s, args) => Console.WriteLine("I should print only once!"));
variableOfSomeType.ListenOnce<InterestingEventArgs>("SomeOtherEvent",
(s, args) => Console.WriteLine("I should print only once!"));
If you can use the Reactive Extensions for .NET, you can simplify this.
You can make an Observable from an event, and only listen for the first element using .Take(1), to do your small snippet of code. This turns this entire process into a couple of lines of code.
Edit: In order to demonstrate, I've made a full sample program (I'll paste below).
I moved the observable creation and subscription into a method (HandleOneShot). This lets you do what you're attempting with a single method call. For demonstrating, I made a class with two properties that implements INotifyPropertyChanged, and am listening for the first property changed event, writing to the console when it occurs.
This takes your code, and changes it to:
HandleOneShot<SomeEventArgs>(variableOfSomeType, "SomeEvent", e => {
// Small snippet of code here
});
Notice that all of the subscription/unsubscription happens automatically for you behind the scenes. There's no need to handle putting in the subscription manually - just Subscribe to the Observable, and Rx takes care of this for you.
When run, this code prints:
Setup...
Setting first property...
**** Prop2 Changed! /new val
Setting second property...
Setting first property again.
Press ENTER to continue...
You only get a single, one shot trigger of your event.
namespace ConsoleApplication1
{
using System;
using System.ComponentModel;
using System.Linq;
class Test : INotifyPropertyChanged
{
private string prop2;
private string prop;
public string Prop
{
get {
return prop;
}
set
{
if (prop != value)
{
prop = value;
if (PropertyChanged!=null)
PropertyChanged(this, new PropertyChangedEventArgs("Prop"));
}
}
}
public string Prop2
{
get
{
return prop2;
}
set
{
if (prop2 != value)
{
prop2 = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Prop2"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
class Program
{
static void HandleOneShot<TEventArgs>(object target, string eventName, Action<TEventArgs> action) where TEventArgs : EventArgs
{
var obsEvent = Observable.FromEvent<TEventArgs>(target, eventName).Take(1);
obsEvent.Subscribe(a => action(a.EventArgs));
}
static void Main(string[] args)
{
Test test = new Test();
Console.WriteLine("Setup...");
HandleOneShot<PropertyChangedEventArgs>(
test,
"PropertyChanged",
e =>
{
Console.WriteLine(" **** {0} Changed! {1}/{2}!", e.PropertyName, test.Prop, test.Prop2);
});
Console.WriteLine("Setting first property...");
test.Prop2 = "new value";
Console.WriteLine("Setting second property...");
test.Prop = "second value";
Console.WriteLine("Setting first property again...");
test.Prop2 = "other value";
Console.WriteLine("Press ENTER to continue...");
Console.ReadLine();
}
}
}
Another user encountered a very similar problem, and I believe the solution in that thread applies here.
In particular, what you have is not an instance of the publish/subscribe pattern, its a message queue. Its easy enough to create your own message queue using a Queue{EventHandler}, where you dequeue events as you invoke them.
So instead of hooking on to an event handler, your "one-shot" events should expose a method allowing clients to add an function to the message queue.
Does it work? If so, then I say go for it. For a one-shot event that looks to be quite elegant.
What I like...
If s is garbage collected, so will the event handler.
The detaching code is right next to the attaching code, making it easy to see what you are are doing.
You might be able to generalize it, but I'm not entierly sure how to because I can't seem to get a pointer to a event.
Personally, I just create a specialized extension method for whatever type has the event I'm dealing with.
Here's a basic version of something I am using right now:
namespace MyLibrary
{
public static class FrameworkElementExtensions
{
public static void HandleWhenLoaded(this FrameworkElement el, RoutedEventHandler handler)
{
RoutedEventHandler wrapperHandler = null;
wrapperHandler = delegate
{
el.Loaded -= wrapperHandler;
handler(el, null);
};
el.Loaded += wrapperHandler;
}
}
}
The reason I think this is the best solution is because you often don't need to just handle the event one time. You also often need to check if the event has already passed... For instance, here is another version of the above extension method that uses an attached property to check if the element is already loaded, in which case it just calls the given handler right away:
namespace MyLibraryOrApplication
{
public static class FrameworkElementExtensions
{
public static void HandleWhenLoaded(this FrameworkElement el, RoutedEventHandler handler)
{
if ((bool)el.GetValue(View.IsLoadedProperty))
{
// el already loaded, call the handler now.
handler(el, null);
return;
}
// el not loaded yet. Attach a wrapper handler that can be removed upon execution.
RoutedEventHandler wrapperHandler = null;
wrapperHandler = delegate
{
el.Loaded -= wrapperHandler;
el.SetValue(View.IsLoadedProperty, true);
handler(el, null);
};
el.Loaded += wrapperHandler;
}
}
}
You probably want to work with the new async/await idioms.
Usually when I need to execute an event handler one-shot like you described, what I really need is something like:
await variableOfSomeSort.SomeMethodAsync();
//small snippet of code here
Why not do use the delegate stack built into the event?
Something like...
private void OnCheckedIn(object sender, Session e)
{
EventHandler<Session> nextInLine = null;
lock (_syncLock)
{
if (SessionCheckedIn != null)
{
nextInLine = (EventHandler<Session>)SessionCheckedIn.GetInvocationList()[0];
SessionCheckedIn -= nextInLine;
}
}
if ( nextInLine != null )
{
nextInLine(this, e);
}
}