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 using Unity 5.0.
I am trying to download some file using C# web client asynchronous.
File gets downloaded and DownloadDataCompleted events also gets fired.
Then I try to do some calculation but I get these error.
RandomRangeInt can only be called from the main thread.
Constructors and field initializers will be executed from the loading thread when loading a scene.
Don't use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function.
Any solution to above problem or can anyone tell me how to run that calculation part on main thread
As requested code is here
private void DownloadXMLFromServer()
{
//first download the file from server
WebClient _maClient = new WebClient ();
_maClient.DownloadDataCompleted += delegate(object sender, DownloadDataCompletedEventArgs e)
{
File.WriteAllBytes("DownloadedXML.xml",e.Result);
DownloadImgOfApp();
};
_maClient.DownloadDataAsync (new Uri (instance.urlOf_XML));
}
public void DownloadImgOfApp()
{
int appNumber = UnityEngine.Random.Range (0, totalNumbOfAppsAvaialbeInXML);//these line throws error
string appName = "App" + (appNumber + 1);
string downloadImgLinkName = null;
string clickableLink = null; }
A lot of methods in Unity3D can be called just from the main thread. This means they can only be called in Start() Update() Awake() OnTriggerEnter(...) and so on. Unity will not let you call these methods from different threads (including asynchronous callbacks).
This makes sense for things like GameObject.Instatiate(...), but I cannot think of a reason why random could cause problems. But for some reason Unity is blocking calls anyway.
To solve your problem, you can create an instance of System.Random and generate your random numbers with it.
The only Problem I can see is that System.Random cant give you a float, just bytes, int or double.
Related
Installed Unity Version is 2020.2.1f1
I'm developing an online multiplayer game using Unity. I have 2 different connection types; Http and Websocket(using Websocketsharp: https://github.com/sta/websocket-sharp, I've installed it using Nuget). Http get/post, Websocket connect/send/close works totally fine but when I call function (directly or by sending events) after response, most of the time my code inside that called function is not completed on editor(works fine on builds-android,ios). I'm not sure if this problem is related to http/websocket but it's related to online connection because I've experienced the same issue when I was developing a real-time multiplayer game using Unity & Gamesparks.
I can do basic functions like print etc. until I call one of the following functions loadscene, update UI, or call another function in same/different script(only first one of these works, everything after that won't run). Console doesn't show any error and game doesn't pause or crash. It runs on same fps but that part of the code won't continue. Other scripts/updates works fine. I've searched a lot but couldn't even find a related topic.
WebSocket Client:
void OnBattleResponse(object sender, MessageEventArgs messageEventArgs) {
string message = messageEventArgs.Data;
if (messageEventArgs.Data.Equals(Constants.WEBSOCKET_ERROR))
return;
var webSocketData = JsonUtility.FromJson<BattleResponseSerializable>(message);
switch (webSocketData.type) {
case 0:
seed = webSocketData.user1.seed;
opponentSeed = webSocketData.user2.seed;
_connectionManager.BattleGetStart(seed, opponentSeed);
break;
.
.
.
}
Connection Manager:
public void BattleGetStart(int userSeed, int opponentSeed) {
_gameManager.StartWave(userSeed, opponentSeed);
}
Game Manager
public void StartWave(int userSeed, int opponentSeed)
{
UserSeed = userSeed;
OpponentSeed = opponentSeed;
UserRandom = new System.Random(UserSeed);
OpponentRandom = new System.Random(OpponentSeed);
StartWaveTimer(); // After this nothing will be run, it's not related to startWaveTimer, any function that I've mentioned causes the same issue.
print("DOESN'T WORK"); // This doesn't work on Editor but works on builds
_spawner.ActivateSpawner(true); // doesn't work
}
I've found a trick to handle this situation but I didn't like it as a proper solution and don't wanna spend resources for Update calls. When I call function (directly or by sending events) after response, inside the called function I set bool to true and in Update call I call the necessary function when the bool is true and the code works totally fine.
---The Trick---
Game Manager
bool startWave;
public void StartWave(int userSeed, int opponentSeed)
{
UserSeed = userSeed;
OpponentSeed = opponentSeed;
UserRandom = new System.Random(UserSeed);
OpponentRandom = new System.Random(OpponentSeed);
startWave = true;
}
void Update() {
if (startWave) {
startWave = false;
StartWaveTimer();
print("WORKS"); // With this trick, it works on editor too.
_spawner.ActivateSpawner(true); // works
}
}
What can be the reason of my script working on builds but not on unity editor?
Thank you!
Advice for those who will experience that kind of issue:
As I've found out Unity is not Thread safe. Unity limits us on calling their API from another Thread, which causes this issue.
So you have to call your functions/methods from main thread instead of another thread.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 3 years ago.
Improve this question
I am currently developing an application in WinForm C# to display cyclically values from a device.
Here is a short example:
public partial class MainForn : Form
{
private System.Windows.Forms.Timer timer1;
public MainForn()
{
InitializeComponent();
timer1 = new System.Windows.Forms.Timer(this.components);
timer1.Enabled = true;
timer1.Tick += new System.EventHandler(this.timer1_Tick);
}
private void timer1_Tick(object sender, EventArgs e)
{
ReadDeviceData();
label1.Text = Convert.ToString(ReadDeviceData());
}
private int ReadDeviceData()
{
Thread.Sleep(300);//Simulation of long treatment for reading
Random rnd = new Random();
return rnd.Next();
}
}
In this example, UI is freezing during the 300ms of ReadDeviceData().
What is the best way to make ReadDeviceData() asynchronous, knowing that this method will be executed endless?
Thank you.
Complementary informations:
This communication is done with a CNC Fanuc which deliver an API which is a DLL:
Example inside ReadDeviceData() there is, in my soft, the following method to read variables:
FWLIBAPI short WINAPI cnc_wrmacror2(unsigned short FlibHndl, unsigned long s_no, unsigned long *num, double *data);
FlibHndl [ in ]
Specify the library handle. See "Library handle" for details.
s_no [ in ]
Specify the start custom macro variable number.
num [ in/out ]
Specify pointer to the number of custom macro variable.
The number which was written actually is returned.
data [ in ]
Pointer to the data of custom macro variable.
Disclaimer:
as I understood OP has control over the ReadDeviceData, since make and not call.
and
This is certainly not the "best" way, but as from the comments, its my best effort with the amount of info provided. If you have no control over the method ReadDeviceData, then Task.Run can be an option. But it leaves you with the timer tick issue... you might want semaphores or concurrent queues... in all means not the best solution either.
The best solution, IMO, would be to create a service class. Call your hardware async in a loop and drop the data in a variable or buffer.
Then, in you UI part: read this value based on an event or timer etc; and use it to update the UI.
It will give you some benefits; for one, your service could also do other stuff, like: flush the data to a database, independent of the UI.
Do note; depending on your data from the device this can be tricky. Its basically how a web cam works, and it's often a hurdle to get the buffer reading/writing correctly if you're implementing that yourself.
original post:
This should do the trick:
//random is seeded based on current time; so best to do it once
Random rnd = new Random();
//the has the extra async keyword
private async void timer1_Tick(object sender, EventArgs e)
{
//await this call because you need its data
var data = await ReadDeviceData();
//set the data; note: use the variable here
label1.Text = Convert.ToString(data);
}
//signature changed
private async Task<int> ReadDeviceData()
{
//await a Task.Delay. (Thread.Sleep is thread blocking)
await Task.Delay(300);//Simulation of long treatment for reading
return rnd.Next();
}
So this is with the aid of your simulation.
If you are actually contacting the hardware; hopefully it has an API which has some Task based methods. If so; its easy, if not: you'll need to convert it yourself, which is tricky.
In that case we need more info on the device API.
This question already has answers here:
FileStream locking a file for reading and writing
(3 answers)
Closed 4 years ago.
I have the following variable:
private readonly object fileLock = new object ();
used in the following function:
public void LogRaw (string message)
{
lock (fileLock)
{
using (StreamWriter streamWriter = new StreamWriter (Filename, true))
{
streamWriter.Write (message);
streamWriter.Flush ();
streamWriter.Close ();
streamWriter.Dispose ();
}
}
}
This function is called by different Tasks executed at the same time. I don't understand why after a while I am receiving this exception:
System.IO.IOException: 'The process cannot access the file 'logs\dashboard.log' because it is being used by another process.'
since I am using the lock keyword and since inside the function I close and dispose the streamWriter.
Thanks in advance for any help.
Edit: Just to clarify, this is a single application with multiple Tasks and therefore multiple threads, but the application is one, I am not running multiple instances of the same app.
Furthermore the Logger class is instantiated only once and reused by the various Tasks that need it (maybe like a singleton).
In the end I found out that it was OneDrive fault. I paused it and run the application for about 10 minutes and didn't receive any exception (Before it was happening after 1 minute max). As soon as I resumed OneDrive (with the application running), after 5 or 6 seconds I got the exception. I didn't think about OneDrive because I thought it is built to handle such a situation, but probably not.
I need to write code in C# that sends data from Serial to an Arduino every 2 seconds.
This is what I tried to do:
Thread sender = new Thread(voidSender);
public static void voidSender() {
serialArduino.WriteLine("Test");
Thread.Sleep(2000);
}
In your example you are starting a thread running through the defined method, sending a single message. To send the message multiple times you need to add a loop to this method like
public void voidSender()
{
//Send forever
while(true)
{
serialArduino.WriteLine("Test");
Thread.Sleep(2000);
}
}
However, periodically events are typically done by using a timer. Simply initialize a timer like
System.Timers.Timer sendMessageTimer = new System.Timers.Timer(2000);
sendMessageTimer.Elapsed += OnSend;
sendMessageTimer.AutoReset = true;
sendMessageTimer.Enabled = true;
Inside the elapsed handler you can send the message like
private void OnSend(Object source, ElapsedEventArgs e)
{
serialArduino.WriteLine("Test");
}
Honestly I don't why you would mix open source platform such as Arduino with C# .net if its not running on core, but that's your concern not mine, I personally wouldn't do that.
if you want to send command every two seconds, I would advise you to use windows scheduler that's integrated in every windows system, however you can implement a clock on your own very easy, still why would you need to put such heavy load of 2 seconds timeframe when it will be almost notable by ordinary user. If you don't want to use clock based system you can use this modified example however it may throw stackoverflow exception sometime since its recursion and you must aways stay away from them if you plan to use this on the long run.
//First sorry for reusing your code but I am writing from my smartphone and
//I am kind of lazy here is quick modification that would work but not the most clever way around
Thread sender = new Thread(voidSender);
public static void voidSender()
{
start:
serialArduino.WriteLine("Test");
Thread.Sleep(2000);
goto start;
}
I'm working on a project that is designed to play both audio and video files through a WPF Window through a MediaElement. This is the xaml for the window:
<Window x:Class="HomeSystem_CSharp.MediaWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MediaWindow" MinHeight="480" MinWidth="720" WindowStyle="None" ResizeMode="NoResize" Visibility="Visible" Cursor="None">
<Grid Background="Black">
<MediaElement LoadedBehavior="Manual" HorizontalAlignment="Stretch" Name="video" VerticalAlignment="Stretch" Cursor="None" MinHeight="480" MinWidth="720"/>
</Grid>
</Window>
This creates the window with no borders, that I plan on full-screening in the future. For now though, I want more room on my desktop. Here is my code for controlling my MediaElement:
private bool playing = false;
public MediaWindow(string dir)
{
InitializeComponent();
video.Source = new Uri(dir);
play();
}
public void play()
{
if (playing)
return;
if (!this.IsVisible)
this.Show();
video.Play();
playing = true;
}
This MediaWindow is created outside of the object, just by a simple MediaWindow mw = new MediaWindow("C:\\test.mp4");
No matter how i've moved stuff around in my code, upon launch EVERY time the GUI is unresponsive, but sound plays. I can hear the video in the background, but there is a broken window on my screen. Just a black box.
The biggest issue is that just the other day it was working fine, and suddenly it broke, and I have no clue what happened. I'm kinda new to c#, so I dont know a TON about what's going on, but I've worked with java for several years so I'm not totally new. Can anyone point out what I'm doing wrong? i can provide any other details but I think i got everything necessary to answer. Thank you for any help, this has been bothering me all day with no fix!
EDIT: Turns out, if I use
public void play()
{
if (playing)
return;
//if (!this.IsVisible)
// this.Show();
video.Play();
new Application().Run(this);
playing = true;
}
instead, it will run the GUI. However, that hangs up the console. Originally I fixed that hang up by using this.Show(), but now that's not working. I know that moving the whole project into a WPF project would fix this, however I'm really trying not to for other reasons. Only win32 for now. Any ideas why this is happening and how to fix it? I do have [STAThread] over my main function if that makes a difference.
EDIT 2:
This video file I'm playing is movie length, and runs perfectly in any other software to prevent that from being an issue with development. As for the MediaWindow creation. What I did is made a win32 console project and set up the user commands there. I then made a new WPF project, and created an xaml gui window. I took those code files, and copied them into the win32 project, and call it to launch in the main method with the MediaWindow mw = new MediaWindow("C:\\test.mp4"); I did it this way because for now I'm trying to keep away from using a pure WPF application, and because I'm kinda new to C# so I wasnt sure how to create the window I wanted without my copy paste method.
No matter how i've moved stuff around in my code, upon launch EVERY time the GUI is unresponsive, but sound plays.
I've managed to reproduce this. One important thing missing in your description is the exact way you create and show the window in your main() method. For example, the following freezes the video leaving the sound playing:
[STAThread]
static void Main(string[] args)
{
var w = new MediaWindow();
w.Show();
Console.ReadLine();
}
The next one "freezes" the console until you close the window:
[STAThread]
static void Main(string[] args)
{
var w = new MediaWindow();
w.ShowDialog();
Console.ReadLine();
}
And this gives you both working:
static void Main(string[] args)
{
var thread = new Thread(ShowMediaWindow);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
while (true) // Just to test console working
{
Console.Write("\r" + DateTime.Now);
Thread.Sleep(100);
}
}
static void ShowMediaWindow()
{
new MediaWindow().ShowDialog();
}
As you can see, the console and the WPF window simply can't work properly in a single thread.
The window class is as simple as this, by the way (the XAML is mostly the same as yours):
public partial class MediaWindow : Window
{
public MediaWindow()
{
InitializeComponent();
video.Source = new Uri(#"C:\video\test.asf");
Play();
}
public void Play()
{
video.Play();
}
}
I guess that'll do for showing a video player window from console.
OK, the whole hybrid console/GUI thing is a new one on me but I'm just going to assume there's a real need to do things that way.
The Application.Run method doesn't return until the application closes. That's why your console is locked up.
Don't create the Application object inside the window. Do it externally. Also, spawn another thread to kick off video playback. This will leave your console responsive.
I'm not gonna get heavy-duty into describing threading and delegates and so forth... you can look that up if you want. I'm just gonna go over what you need to do for this specific example. Somewhere in the class that launches the video, but not in a method, define a delegate type like this:
delegate void LaunchVideo(String s);
A delegate is essentially kind of a pointer to a function with a certain definition of return value and parameters. Here we've defined the delegate type as a function that takes a String parameter and returns nothing. Next, at the point in your code where you want to play the video, do this:
LaunchVideo lv = new delegate(String vidfile)
{
Application app = new Application();
app.Run(new MediaWindow(vidfile));
};
IAsyncResult result = lv.BeginInvoke( "C:\\vid.mp4", myVideoCompleted, null );
This creates the delegate variable and points it at an anonymous function that creates the app and launch video playback. Then it calls the delegate's BeginInvoke method, which is part of the basic delegate class. This spawns a new thread running in the function pointed to by the delegate.
Note that calling Application.Run with a window parameter like this will open the window but it won't call the play() method. You may want to move that code to the constructor, or add a call to it in the constructor.
Be aware that your main thread cannot safely call methods in objects created in the invoked thread unless you use the lock function to make things thread safe.
If you need "open" and "play" to be separately controlled events which are both invoked by the console then you'll have to figure out a means to pass messages from the console thread to the window thread.
The parameter list for BeginInvoke always starts off with whatever parameters are expected by the function you're invoking. So in this case, that's the string with the video filename. Next is the name of a callback function which will be called when the invoked function exits. It's a void function that takes an AsyncResult parameter, like this:
void myVideoCompleted(AsyncResult result)
{
// Do whatever here... Be aware this is still on the other thread
}
You can use 'null' instead of a function name, if you don't need anything called at the end. Be aware that if you do use a callback function, it runs on the new thread started by BeginInvoke, not the thread that called it.
The last parameter to BeginInvoke is an object that will be passed through to the callback function via the AsyncState member of the AsyncResult parameter. You can pass 'null' if you're not using a callback or if you have no parameters which will be needed by the callback.
You can also call the EndInvoke method of the delegate to get back any results that may've been returned by the function. However, be aware that this will block if the invoked function isn't finished yet. In this case you have no results to worry about.