This question already has answers here:
Use Unity API from another Thread or call a function in the main Thread
(5 answers)
Closed 6 years ago.
I am starting to learn some unity and I have a quick question in regards to multithreading.
I have a class which contains an instance of a datamodel class. The primary function of the class is the fetch a remote resource asynchronously and update the datamodel class. Then in the update function the datamodel class is read out and written to some text in a gameobject.
Anyway I am worrying that it could cause some issues related to multithreading since my async updating of the class could run at the same time as the update function causing a race condition. Do I have to wrap access to the class in a mutex?
class Data {
public int Number { get; set; }
public string Name { get; set; }
}
class Network : MonoBehaviour {
private Data d;
public void Start() {
// setting up handler to async fetch data and call provided callback
Networking.GetData(s => ParsePayload(s));
}
private void ParsePayload(string payload) {
d = JsonConvert.DeserializeObject<Data>(payload);
}
public void Update() {
var label = GameObject.Find("textObject").GetComponent<Text>();
label.Text = d.Name;
}
}
So am I right in this or does unity handle this by itself?
thx for any advice!
So am I right in this or does unity handle this by itself?
You are right in this. It could cause some problems and Unity does not handle this by itself. You are responsible to making sure that only one Thread can access d variable at a time. There are several ways of this but using the lock keyword can accompilsh this.
Since d is updated in the ParsePayload function and accessed in the Update function, you have to use the lock keyword in both functions.
Another unrelated problem is the GameObject.Find("textObject"). This should be done in the Start() function and saved to a variable. Don't do that in the Update function each frame.
class Network : MonoBehaviour
{
private Data d;
Text label;
private System.Object threadLocker = new System.Object();
public void Start()
{
label = GameObject.Find("textObject").GetComponent<Text>();
// setting up handler to async fetch data and call provided callback
Networking.GetData(s => ParsePayload(s));
}
private void ParsePayload(string payload)
{
lock (threadLocker)
{
d = JsonConvert.DeserializeObject<Data>(payload);
}
}
public void Update()
{
lock (threadLocker)
{
label.text = d.Name;
}
}
}
Related
I am working on a WPF Application that was running smooth until I added threadding. I wanted to ease the saving/autosaving process by putting it into a BackgroundWorker so my UI is not blocked while saving occours.
Think of my App as a custom photobook maker.
Lets assume my UI consists of several Image Objects. The Source for those images lies within a custom PhotobookImageObject because each selected Image also contains additional Metadata.
PhotobookImageObject
public class PhotobookImageObject
{
public BitmapSource source { get; set; }
public String unimportantMetadata{ get; set; }
}
When I want to save, I want to save the complete Photobook. For simplicity:
Photobook
public class Photobook: INotifyPropertyChanged
{
public List<PhotobookImageObject> Photos{ get; set;}
public String otherMetaData { get; set;}
}
My Saving process worked when not using Threads. But ever since Im running it in a BackgroundWorker I can not access the PhotobookImageObjects in the list anymore.
Now I know the Thread for Saving can not Acces Objects from different Threads. This is why I am using a custom Class to Push the object into the thread of the BackgroundWorker. I found this solution here: How do you pass a List<> of Lists<> to a background worker?
Here is the acutal code:
Setting up the Worker:
private static BackgroundWorker saveWorker = new BackgroundWorker();
private static void saveWorkerExec(Photobook book, String Location, bool notAuto)
{
saveWorker.DoWork += doWork;
saveWorker.WorkerSupportsCancellation = true;
saveWorker.RunWorkerCompleted += (s, o) =>
{
Helper.Message("Photobook saved");
if (o.Error != null)
{
MessageBox.Show("There was an error while saving! \n\n" + o.Error.ToString());
}
};
BGObj obj = new BGObj
{
bk = book,
Loc = Location,
not = notAuto
};
saveWorker.RunWorkerAsync(obj);
}
The custom class I use to transfer the Data:
public class BGObj
{
public Photobook bk { get; set; }
public String Loc { get; set; }
public bool not { get; set; }
}
And the actual part where the BackgroundWorker should receive the class into his own Thread:
private static void doWork(object sender, DoWorkEventArgs e)
{
BGObj received = e.Argument as BGObj;
Photobook book= received.bk;
String Location = received.Loc;
bool notAuto = received.not;
//this function can not Access the books.Photos.last().source for example.
SaveProjectToContainer(book, Location, notAuto);
}
I am still receiving a System.InvalidOperationException when I try to access the BitmapSource of the PhotobookImageObject in the Photos List in Photobook.
My assumption: the BGobj I am creating is only referencing the actual Photobook so the data of its members is still resting in the wrong thread. How on earth can I make sure all submembers of my objects are actually passed to the Thread where I want to process them? Or am I wrong here and its something else?
Thank you for your time.
Since nobody could help me with the issue I followed Theodors approach and switched to a more modern approach using Async/Await.
From my understanding this is not making the actions run in the background but still freeing the UI thread and running the await" tasks "inbetween" when the thread has capacity free. This allows me to access the UI-elements and still stop the application from freezin a bit while autosaving.
If someone needs a few hints how to solve this think of it like this:
public static void NotMain()
{
// queue following function. void Main for example would continue running after this.
await do_stuff_in_between(var object);
// Stuff after await will only happen if the function is done but UI is not blocked
Console.WriteLine("saving done");
}
public static async Task do_stuff_in_between(var object)
{
// do stuff with object or whatever you want.
}
This question already has an answer here:
Godot Signal "Emitted" but not "Received"
(1 answer)
Closed 2 years ago.
I have a Signal inside a script attached to a node:
Game.tscn/Game.cs
using Godot;
public class Game : Node2D
{
[Signal]
public delegate void AddPowerUp(Powerup powerup);
}
I try to emit a signal from another class:
ExtraBall.tscn/Powerup.cs
public override void _IntegrateForces(Physics2DDirectBodyState state)
{
EmitSignal("AddPowerUp", this);
}
Then in another scene, Level.tscn, I instanced Game and ExtraBall. However, I get an error when I try to emit the signal.
E 0:00:05.828 emit_signal: Can't emit non-existing signal "AddPowerUp".
Here's how it looks like in the engine:
Game has the Signal and I attach to a function in Player:
In the Level scene, I instance Game and ExtraBall:
Shouldn't ExtraBall be able to know about the "AddPowerUp" signal since it was instanced under the same SceneTree? If not, how do I go about emitting a signal that's defined somewhere else?
I ended up doing it a different way.
I removed the Game instance from the Level scene.
Declared the signal inside ExtraBall.cs
Created an instance of Level inside Game
Connected the signal from ExtraBall to a function inside Player through code.
The scene trees are as follows:
Game (Node2D)
Player (Player.tscn)
Level (Node2D)
ExtraBall (Extraball.tscn)
Not sure if this is a good approach, but it seems to work without any issues. I posted a small "game" that shows the solution at the bottom.
Game.cs
public class Game : Node2D
{
[Signal] public delegate void LevelLoaded();
public override void _Ready()
{
PackedScene scene = ResourceLoader.Load("res://source/level/Level.tscn") as PackedScene;
AddChild(scene.Instance());
EmitSignal("LevelLoaded");
}
}
Player.cs
public class Player : KinematicBody2D
{
public override void _Ready()
{
GetNode<Node>("/root/Game").Connect("LevelLoaded", this, "OnLevelLoaded");
}
public void OnLevelLoaded()
{
GetNode<Node>("/root/Game/Level/ExtraBall").Connect("TestSignal", this, "OnTestSignal");
}
public void OnTestSignal()
{
GD.Print("YAY!");
}
}
ExtraBall.cs
public class ExtraBall : RigidBody2D
{
[Signal] public delegate void TestSignal();
public override void _IntegrateForces(Physics2DDirectBodyState state)
{
var bodies = GetCollidingBodies();
foreach (Node2D node in bodies)
{
EmitSignal("TestSignal");
}
}
}
Small project with solution: https://www.dropbox.com/s/v7zac3lripsslrz/SignalTest.zip?dl=0
This question already has answers here:
Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on
(22 answers)
Closed 5 years ago.
Good morning guys, I'm trying to write some application in C#, where I like to update the UI (A progress bar in this case) from another thread and class.
But I just can't get it to work, I googled and search around but I'm afraid I just don't get it. I have a windows form application, I start a thread when I click a button, and somewhere in this thread I would like to update my UI.
I either get:
An object reference is required for the non-static field, method, or property
or something in the direction of the object being created by a different thread.
(At the location where I try to call Form1.UpdateProgressBar(value); in fileReader).
I have no experience in object orienting programming, I usually stick with C. If anyone could tell me the right way to do this, I would be very happy.
Edit_1: Alright.. Combinations of errors, the answer so far might have helped if I didn't have he static issue. And fixing the static issue by making the entire class static creates another X amount of errors on its own, including:
Static classes cannot have instance constructors
namespace TestCode
{
public partial class Form1 : Form
{
static fileReader SourceReader;
public Thread SearchThread { get; set; }
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
folderBrowserDialog1.ShowDialog();
Console.WriteLine(folderBrowserDialog1.SelectedPath);
this.SearchThread = new Thread(new ThreadStart(this.ThreadProcSafe));
this.SearchThread.Start();
}
public void UpdateProgressBar( int value)
{
progressBar1.Value =value;
}
private void ThreadProcSafe()
{
SourceReader = new fileReader(folderBrowserDialog1.SelectedPath);
}
}
}
Class 2:
namespace TestCode
{
class fileReader
{
public fileReader(String path)
{
int value = 20;
/*Do some stuff*/
Form1.UpdateProgressBar(value);
}
}
}
Check if an invoke is required and inf needed then use the controls Invoke function:
public void UpdateProgressBar( int value)
{
if(progressBar1.InvokeRequired){
progressBar1.Invoke(new MethodInvoker(() => progressBar1.Value=value));
}else{
progressBar1.Value =value;
}
}
You can use a MethodInvoker when trying to modify the UI from another class like this:
ProgressBar progressBar = Form1.progressBar1;
MethodInvoker action = () => progressBar.Value = 80;
progressBar.BeginInvoke(action);
while you can use this when working in a different Thread ( a Task for example ):
progressBar1.Invoke((Action)(() => progressBar1.Value=50))
But consider the comments on your post. It's not needed to be dependent on Forms in fileReader
Side note: I don't know how you didn't find your problem on here:
how to update a windows form GUI from another class?
How to update the GUI from another thread in C#?
I have a Class file to perform certain function , for example
public class clsFunction
{
public DataTable FunctionOne()
{
//some code
}
public void FunctionTwo()
{
//Some Code
}
}
SecondClass is use to call function from clsFunction , and this main class in running on a console program with multiple thread.
public class SecondClass
{
public void ThreadOne()
{
while(true){DataTable dt = new clsFunction().FunctionOne;}
}
public void ThreadTwo()
{
while(true){new clsFunction().FunctionTwo();}
}
}
class Main
{
static void Main (string[] args)
{
//Thread to start SecondClass.ThreadOne
//THread to start SecondClass.ThreadTwo
}
}
My concern is will my class value reinitialize to default value when I call new clsFunction() each time. for example , thread two may running it own value , when thread one is call , will all the thread two value change to it default value ?
Maybe you don't understand what new does. It creates an object. It's purpose is not to initialize something that already exists. Objects are independent.
Creating an object has no influence on any other object, except of course if the constructor does something to influence other objects.
If, lets say I have a FormA with a ListView and an Update() function. Then I also have one Math-Class with a function A() wich does some magic... Can a delegate be used to call Update() from A()? Or Is there a better way? I've realized it's risky to update a gui form from another class.... Thanks in advance!
Yes. Its not that risky as long as the Math class has no knowledge of what its actually calling. You just give it a rough idea by pointing it to the desired function from your Form:
public class MathClass {
public Action FunctionToCall { get; set; }
public void DoSomeMathOperation() {
// do something here.. then call the function:
FunctionToCall();
}
}
In your form you would do this:
// Form.cs
public void Update() {
// this is your update function
}
public void DoMathStuff() {
MathClass m = new MathClass() { FunctionToCall = Update };
m.DoSomeMathOperation(); // MathClass will end up calling the Update method above.
}
Your MathClass calls Update, but it has no knowledge of the object that told it to call Update or where Update is.. making it safer than tightly coupling your objects together.