Is it true that event in Unity's Monobehavior is single threaded? When I trigger an event, if one of the listener raises an exception, the catch block will be executed. I assume once class A fires an event, the thread will go over each subscriber. When each subscriber finishes, does class A continue in the same thread?
public class EventExample : MonoBehaviour
{
public delegate void ExampleEventHandler();
public static event ExampleEventHandler OneDayPassed;
public void Start NextTurn()
{
try {
if (OneDayPassed != null)
{
OneDayPassed();
}
}
catch(Exception e)
{
// will catch error here
}
}
}
public class EntityWatcher : MonoBehaviour
{
void Start()
{
EventExample.OneDayPassed += this.PrepareNextDay;
}
public void PrepareNextDay()
{
int test = int.parse("error");
}
}
Monobehavior is mostly single Threaded but some few callback functions are not. Most of the Unity API callback functions will be made on the main Thread.
These are the fnctions that will not be called on the main Thread which means that you can't call/use the Unity API inside these functions:
Application.RegisterLogCallbackThreaded
Application.logMessageReceivedThreaded event
OnAudioFilterRead Monobehavior callback function.
As for your own custom event like:
public delegate void ExampleEventHandler();
public static event ExampleEventHandler OneDayPassed;
The event is called on the Thread you invoked it from. If you call it from the main/MonoBehaviour thread, the callback will happen on the main thread. If you create a new Thread and call it from there or use any of the 3 functions listed above then expect it to be called on another Thread other than the main Thread.
If you need to use Unity's API from another Thread other than the main Thread then see this post.
Unity's event API is single-threaded. And also, is not designed to support multi-threading (not thread-safe).
Of course, you can explicitly delegate some work with multi-threading if you want, but don't try to call the Unity API with these.
You can have some more information here : https://answers.unity.com/questions/180243/threading-in-unity.html
If you need to rely on some execution order, you can refer to the manual page here
Related
I have written a class library outside of Unity (and put it in Unity as a DLL) in which I've declared a public event that I listen to from my unity code. The event is being invoked from within the DLL. When the event has been invoked the methods that subscribe to the event are being executed as I expect, with the exception that UnityEngine.SceneManegement.SceneManager.LoadScene() is not running, as well as causing any code after it to not run.
using UnityEngine.SceneManagement;
using MyDLL; // this namespace has the Client.OnConnected event
public class GameManager : MonoBehaviour
{
void Awake()
{
Client.OnConnected += () => {
Debug.Log("Connected to Server");
SceneManager.LoadScene("Main");
Debug.Log("Main Scene Loaded");
};
}
}
When the Client.OnConnected event is invoked, i can see the "Connected to Server" being logged but the scene "Main" is not being loaded and "Main Scene Loaded" is not being logged.
Does anyone have any idea why this is happening and how to work around it?
Your issue is most probably that most of the Unity API can only be called from the Unity main thread.
Your OnConnected event seems to be called asynchronously.
You will need to dispatch that call back into the Unity main thread.
An often used pattern for this is the following:
public class GameManager : MonoBehaviour
{
// A thread safe Queue we can append actions to that shall be executed in the next Update call
private readonly ConcurrentQueue<Action> _actions = new ConcurrentQueue<Action>();
void Awake()
{
Client.OnConnected += OnClientConnected;
}
private void OnClientConnected()
{
// Instead of immediately exciting the callback append it to the
// actions to be executed in the next Update call on the Unity main thread
_actions.Enqueue(() =>
{
Debug.Log("Connected to Server");
SceneManager.LoadScene("Main");
Debug.Log("Main Scene Loaded");
};
}
// In the main thread work of the dispatched actions
private void Update ()
{
while(_actions.Count > 0)
{
if(_actions.TryDequeue(out var action))
{
action?.Invoke();
}
}
}
}
I am using the C wrapper of UIAutomation to listen for events.
Specifically, I'm looking for a focus event. When that focus event happens, I'm simply logging to the console, and unbinding the event.
Problem is - the program seems to stall/"die" on the automation.RemoveAllEventHandlers() call. The line below that never gets executed (doesn't print to console, breakpoint doesn't get hit.)
My guess is this is a threading issue - automationis created on a thread, but the event gets called on a different thread, and a big issue ensues. Is this the problem? If so/if not - what is and how do I fix it?
Below is the code:
public class FocusListener
{
private readonly CUIAutomation _automation;
public FocusListener()
{
_automation = new CUIAutomation();
_automation.AddFocusChangedEventHandler(null, new FocusChangeHandler(this));
Console.WriteLine("Added a focus event!");
}
public void On_WindowClicked()
{
Console.WriteLine("Window clicked!");
_automation.RemoveAllEventHandlers(); // program seems to die right here..
Console.WriteLine("Focus event removed"); // this line never gets executed..
}
}
public class FocusChangeHandler : IUIAutomationFocusChangedEventHandler
{
private readonly FocusListener _listener;
public FocusChangeHandler(FocusListener listener)
{
_listener = listener;
}
public void HandleFocusChangedEvent(IUIAutomationElement sender)
{
if (sender.CurrentControlType == UIA_ControlTypeIds.UIA_WindowControlTypeId)
{
_listener.On_WindowClicked();
}
}
}
According to: https://msdn.microsoft.com/en-us/library/windows/desktop/ee671692%28v=vs.85%29.aspx
It is safe to make UI Automation calls in a UI Automation event handler, >because the event handler is always called on a non-UI thread. However, when >subscribing to events that may originate from your client application UI, you >must make the call to IUIAutomation::AddAutomationEventHandler, or a related >method, on a non-UI thread (which should also be an MTA thread). Remove event >handlers on the same thread.
A simple question on callbacks. Do callback functions return to the next line in the calling function after completion ?
class A
{
public delegate void A();
public event A onA;
public void func()
{
//some code 1
onA();
//some code 2
}
So the question is will onA event go and execute the respective handler and then come back to 'some code 2' bit or is this asynchronous and code will not wait for the event to be fully handled?
I hope the question is clear.
Thanks
}
The way you used delegate: is synchronous. If you want asynchronous you must invoke delegate with: BeginInvoke method.
Yes, in your example onA() will trigger all over the event handlers hooked up to A to fire. They are just methods that will be called. After they are all called, control will return to func().
It is not asynchronous - you are only using one thread. Everything will happen in a well defined order.
A good way to experiment would be to step through the code in your example using the built in debugger.
Your code isn't assync. But you can Use Delegates Asynchronously.
No, calling a event isn't a assync thing. Your code func() will only continue after onA() ends running.
You would use BeginInvoke or Threading if will wants assync code running.
Read more about delegate invokes here.
As others have pointed out, this is entirely synchronous. If you wanted to execute this asynchronously you would have to write this differently.
Additionally, if the event 'onA' is not subscribed to, onA() will raise a null reference exception.
The usual pattern is to define an event 'Foo' and a method 'OnFoo' which you call when the event occurs. From the name of the event I suspect this is what you desire - e.g.:-
class Foo // Class and member names must be distinct
{
public delegate void ADelegate();
public event ADelegate A;
private void OnA()
{
if(A != null)
A();
}
public void Func()
{
// Some code...
OnA();
// More code...
}
}
If you want to call the subscribed event handlers asynchronously you can use BeginInvoke() and EndInvoke() thus:-
class Foo // Class and member names must be distinct
{
public delegate void ADelegate();
public event ADelegate A;
private void OnA()
{
if (A == null) return;
// There may be multiple subscribers, invoke each separately.
foreach(ADelegate del in A.GetInvocationList())
del.BeginInvoke(SubscriberCallback, del);
}
private void SubscriberCallback(IAsyncResult result)
{
var del = (ADelegate) result.AsyncState;
del.EndInvoke(result);
// Do something in the callback...
}
public void Func()
{
// Some code...
OnA();
// More code...
}
}
Note that this code won't wait to finish executing the event subscriber(s), you would have to thread the async result through the event call to ensure this happens.
Note that the 'callback' is the method you specify in the asynchronous BeginInvoke (since it is 'called back' once the async work is done), and doesn't return to Func() as it is executed in a separate thread.
I have a C# 2.0 application with a form that uses a class that contains a thread.
In the thread function, rather than call the event handler directly, it is invoked. The effect is that the owning form does not need to call InvokeRequired/BeginInvoke to update its controls.
public class Foo
{
private Control owner_;
Thread thread_;
public event EventHandler<EventArgs> FooEvent;
public Foo(Control owner)
{
owner_ = owner;
thread_ = new Thread(FooThread);
thread_.Start();
}
private void FooThread()
{
Thread.Sleep(1000);
for (;;)
{
// Invoke performed in the thread
owner_.Invoke((EventHandler<EventArgs>)InternalFooEvent,
new object[] { this, new EventArgs() });
Thread.Sleep(10);
}
}
private void InternalFooEvent(object sender, EventArgs e)
{
EventHandler<EventArgs> evt = FooEvent;
if (evt != null)
evt(sender, e);
}
}
public partial class Form1 : Form
{
private Foo foo_;
public Form1()
{
InitializeComponent();
foo_ = new Foo(this);
foo_.FooEvent += OnFooEvent;
}
private void OnFooEvent(object sender, EventArgs e)
{
// does not need to call InvokeRequired/BeginInvoke()
label_.Text = "hello";
}
}
This is obviously contrary to the method used by Microsoft APIs that use background threads like System.Timers.Timer and System.Io.Ports.SerialPort. Is there anything inherently wrong with this method? Is it dangerous in some way?
Thanks,
PaulH
Edit: also, what if the form did not subscribe to the event right away? Would it clog the Form's message queue with events the form wasn't interested in?
This is a threadsafe call, the method will be processed in the thread of the form.
Nothing wrong with it when looking at it from a conceptual perspective.
Timers are more elegant for such tasks, though. However, it could be that a timer with an interval of 10ms slows down the GUI, that's probably why Invoke was used.
You do not need a call to InvokeRequired, since it is clear that the Control is in an other thread. Also, BeginInvoke only needs to be called when you want to call a method asynchronously, which obviously isn't the case here.
Regarding your edit:
No, the message queue will not be clogged. No event will be fired if no handler has been registered. Take another look at your code ;)
How can I call a method on a form from a method called from an external class from a backgroundWorker? I believe that delegates are somehow the answer to this question, but after spending time reading, I still am confused by this problem.
This is in Visual Studio 2008, the backgroundWorker is run from the form and calls ExternalClass.Method. The form is in namespace ProgramName and the ExternalClass is using ProgramName. When i declare public delegate MyDelegate in the namespace ProgramName in the file of my windows.form I can create an instance of MyDelegate and call it in a method of my form (but this does not help me), but if I try to create an instance of MyDelegate and call it from a method of my external class I cannot access the method of the windows.form, even though it is public.
thanks
yes, I want to pass progress reports (int percent, string status) back from ExternalClass.Method. Can you explain a bit more about that CSharpAtl (or anyone)?
Please do yourself a favor and read up on the BackgroundWorker Component, especially "How to: Implement a Form That Uses a Background Operation".
Other resources:
Windows Client Development Portal
Using the BackgroundWorker Control (video)
Windows Forms Videos
The main thing to realize is that you actually have two levels of synchronization going on here: between the Form and the BackgroundWorker, and between the BackgroundWorker and the ExternalClass object.
The Form is asynchronously invoking BackgroundWorker.DoWork(), which is running in another thread. Any updates to the Form should come through Form.Invoke() (which fires an arbitrary delegate in the Form's thread) or, better yet, through the BackgroundWorker.ProgressChanged event (which fires a specific event in the Form's thread).
So what you want to do is proxy the status updates from the ExternalClass method back to the BackgroundWorker, which will in turn push them on to the Form. One way I've done this in the past is to use a callback delegate:
public delegate void ProgressCallback(double percentCompleted, string status);
And have my expensive worker method take the callback as an argument:
public void ExpensiveMethod(ProgressCallback callback) {
while(doingThings) {
if(callback != null) callback(percentDone, statusString);
}
}
Then in your BackgroundWorker class, define a method that matches your callback delegate, and have it call BackgroundWorker.ReportProgress() to trigger the BackgroundWorker.ProgressChanged event, which can in turn update your Form's state.
Update: this is basically the same as the solution Henk Holterman suggested in his new edit.
Note that your question (afaik) is not just about the backgroundwiorker but just as much about how to break a circular reference between classes. This is a standard problem with a standard solution.
You can pass a delegate (referring to a Form-method) around just as any object so also to a Backgroundworker. And the Bgw can pass it to the external method. A delegate includes a reference to the object (in this case the Form).
Note that since you are on another thread you will need to use Control.Invoke inside the delegate, or use the Bgw ReportProgress event.
public partial class Form1 : Form
{
private void ReportProgresshandler(int percent, string state)
{
backgroundWorker1.ReportProgress(percent); // also does the Invoke
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
var ex = new ExampleClass();
ex.Dowork(ReportProgresshandler);
}
}
and something like
class ExampleClass
{
delegate void ReportDelegate(int percent, string status);
public void Dowork(ReportDelegate report)
{
report(0, "starting");
}
}
I'm not sure what the trouble is. And also you can use a delegate, but don't need one.
using System.Windows.Forms;
using System.ComponentModel;
public partial class ExampleForm : Form
{
public ExampleForm()
{
InitializeComponent();
var worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(doWork);
worker.RunWorkerAsync(this);
}
void doWork(object sender, DoWorkEventArgs e)
{
ExampleForm f = e.Argument as ExampleForm;
f.Hello();
}
private void Hello()
{
}
}