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.
Related
I have a Windows forms application that I am trying to add accessibility to and have run into an issue with the speech synthesizer where it appears that the SpeechAsyncCancelAll runs in the user interface thread. Performance is totally dependent on the power of the PC.
This can be reproduced with a very simple application in Windows forms.
Create a form and add a numeric up down control. Then use this code:
using System.Windows.Forms;
using System.Speech;
using System.Speech.Synthesis;
namespace WindowsFormsApp8
{
public partial class Form1 : Form
{
SpeechSynthesizer _speech = new SpeechSynthesizer();
public Form1()
{
InitializeComponent();
}
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
_speech.SpeakAsyncCancelAll();
_speech.SpeakAsync(numericUpDown1.Value.ToString());
}
}
}
On my development machine which is very powerful it runs without a problem and very fast when you hold down the up arrow. Each value is cancelled so you do not hear anything as the control increments and when you stop pressing the up arrow it announces the last value properly.
However, the minute this is run on a lesser PC, even a core i9 hexacore machine, the repeat on the increment slows to a crawl.
It looks to me that this is running on the user interface thread.
Any suggestions?
Thanks
Don't get yourself tricked by the "Async" in the name of the SpeakAsyncCancelAll() method name. As one can see in the source code of the SpeechSynthesizer and VoiceSynthesis classes, there is quite some synchronous code involved in order to communicate with a background thread that does the actual voice synthesis. This code is actually quite heavy in that it uses multiple lock statements.
A best practice solution for this situation (multiple successive user interactions could create a series of code reactions but in the end we only want the last one) is to not directly start the reaction, but start a timer and only perform the reaction if there was no other user interaction in the meantime.
public partial class Form1 : Form
{
private SpeechSynthesizer _speech = new SpeechSynthesizer();
public Form1()
{
InitializeComponent();
timer1.Interval = 500;
}
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
// Reset timer
timer1.Stop();
timer1.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Stop();
_speech.SpeakAsyncCancelAll();
_speech.SpeakAsync(numericUpDown1.Value.ToString());
}
}
You should allow the user to configure the timer interval to chose a good compromise based on their system performance and their individual usage patterns. People who need audio assistance often consider for good reasons a too long delay between user activity and an audio response as wasting their time. So it is important that users can configure such a delay to best fit their individual needs.
Let's assume you have taken Neil's excellent comment into consideration, and checked the repeat rate of the NumericUpDown control on the other PCs "without" calling the speech engine. Good.
Your code looks right. The SpeakAsyncCancelAll and SpeakAsync do not block and are "expected" to be running on a background thread. When I attempted to reproduce the problem (not a shocker) your code works fine on my PC using the test condition you describe. That being the case, maybe you could try a couple of variations on the slim chance that something makes a difference and yields some kind of clue by ruling out some unlikely issues.
Variation 1
Capture the "text to say" and post the work using BeginInvoke. This ensures that nothing could possibly be interfering with the ValueChanged or MouseDown messages from pumping in the message queue.
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
// Make 100% sure that the up-down ctrl is decoupled from speak call.
var say = $"{numericUpDown1.Value}";
// Ruling out an unlikely problem
BeginInvoke((MethodInvoker)delegate
{
_speech.SpeakAsyncCancelAll();
_speech.SpeakAsync(say);
});
}
Variation 2
Since you have a suspicion that something is running on the UI thread that shouldn't be, go ahead and give explicit instructions to post it on a background Task. At least we can rule that out.
private void numericUpDown2_ValueChanged(object sender, EventArgs e)
{
// Make 100% sure that the up-down ctrl is decoupled from speak call.
var say = $"{numericUpDown2.Value}";
// Ruling out an unlikely problem
Task.Run(() =>
{
_speech.SpeakAsyncCancelAll();
_speech.SpeakAsync(say);
});
}
Variation 3 - Inspired by NineBerry's answer (added to test code project repo)
/// <summary>
/// Watchdog timer inspired by NineBerry.
/// https://stackoverflow.com/a/74975629/5438626
/// Please accept THAT answer if this solves your issue.
/// </summary>
int _changeCount = 0;
private void numericUpDown3_ValueChanged(object sender, EventArgs e)
{
var captureCount = ++_changeCount;
var say = $"{numericUpDown3.Value}";
Task
.Delay(TimeSpan.FromMilliseconds(250))
.GetAwaiter()
.OnCompleted(() =>
{
if(captureCount.Equals(_changeCount))
{
Debug.WriteLine(say);
_speech.SpeakAsyncCancelAll();
_speech.SpeakAsync(say);
}
});
}
Well the above answers do not solve the issue. However, all the tested computers were dell computers. By default when the OS is installed, Dell installs a sound utility called MaxWaves which allows different audio enhancements. Although all options are off in this utility, it appears that it buffers the sound and when an Async.CancelAll() call comes, it blocks until the sound duration is complete. Therefore everything appears to slow to a crawl.
Uninstalling this utility as well as disabling it as a service corrects the problem.
Everything now works correctly. Thank you for your answers.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
I have a windows forms app that works well on my development machine. However, I see strange behavior trying to run multiple tasks in parallel after publishing the application. There is no error, but it doesn't work as expected. Here is the code:
private async void Button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
try
{
var watch = Stopwatch.StartNew();
textBox1.Text = $"Processing...";
await SyncAppDbAsync();
watch.Stop();
var time = watch.ElapsedMilliseconds;
textBox1.Text = $"End successfully. Minutes: {String.Format("{0:0.00}", (double)(time / 1000) / 60)}";
}
catch (Exception ex)
{
textBox1.Text = $"Message: {ex.Message}, Source: {ex.Source}, HResult: {ex.InnerException}";
}
}
public async Task SyncAppDbAsync()
{
//delete tables rows
// I block the UI for some seconds because not want to write
// a record if is not deleted
Task.WaitAll(
AgenteApp.RemoveAllAgentiAppAsync(),
RubricaApp.RemoveAllRubricheAppAsync(),
...
);
//read data da from database
var readAgents = Task.Run(Agent.GetAgentAsync);
var readAddressBooks = Task.Run(AddressBook.GetAddressBookAsync);
...
await Task.WhenAll(
readAgents,
readAddressBooks,
...
);
//save data on sqlite database(myDb.db)
var addAgenti = Task.Run(async () =>
{
var progrIndicator = new Progress<int>(AgentiProgress);
var agenti = AgenteApp.FillAgentiAppFromCompanyAsync(await readAgents, progrIndicator);
await AgenteApp.AddAgentiAppAsync(await agenti);
});
var addRubriche = Task.Run(async () =>
{
var progrIndicator = new Progress<int>(RubricheProgress);
var rubriche = RubricaApp.FillRubricheAppFromCompanyAsync(await readAddressBooks, progrIndicator);
await RubricaApp.AddRubricheAppAsync(await rubriche);
});
await Task.WhenAll(
addAgenti,
addRubriche,
...
);
}
Each task in that code corresponds to a table in an sqlite database. The code reads data from one sqlite database and writes to another sqlite database.
I expect this code to take a few minutes to run. In the meantime, there is a progress bar for each table that should update. Instead, the code runs in just a few seconds, the progress bars never update, and the database tables are unchanged. I see this text in my textbox at the end: End successfully. Minutes: 0,02.
What can I do to understand the problem and fix it? Again, this works correctly on my development machine.
UPDATE:
Sorry to everyone: code works perfectly fine! I make stupid mistake
with a path of sqlite database.
I hardcoded in app.config:
I accept suggests on how make dynamic that path
So again sorry
There's not enough information in the question at the time I'm writing this for me to evaluate the problem. But I can at least give some strategies that will help you find a solution on your own:
Add excessive and detailed logging to the code (You can remove it later). That will help you understand what is happening as the program runs, and potentially see where it goes wrong. Run this in production if you have to, but preferably:
If you don't already have one, get a staging or QA environment separate from your own machine, (use a local VM if you really have to) where you can reproduce the problem on demand, away from production. The logging information from the previous step may help with this.
Look for exceptions that might be hidden by the async code. Make sure you're checking the result of each of those operations.
Remove most of the code. The program will be incomplete, but it will run that incomplete section as expected. Keep adding more small chunks of the complete program back until it breaks again. At this point, you will (probably) know where the issue is... though it could be a race condition caused by an earlier block, but at least you'll have a clue where to start looking.
Unroll the async code and run everything using traditional synchronized methods. Make sure the simple synchronized code works in the production environment before trying to adding parallelism.
When you finally track down this issue, make sure you have a unit test that will detect the problem in the future before it goes to production, to avoid a regression.
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;
}
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.
I'm building an app that uses and scanner API and a image to other format converter. I have a method (actually a click event) that do this:
private void ButtonScanAndParse_Click(object sender, EventArgs e)
{
short scan_result = scanner_api.Scan();
if (scan_result == 1)
parse_api.Parse(); // This will check for a saved image the scanner_api stores on disk, and then convert it.
}
The problem is that the if condition (scan_result == 1) is evaluated inmediatly, so it just don't work.
How can I force the CLR to wait until the API return the convenient result.
NOTE
Just by doing something like:
private void ButtonScanAndParse_Click(object sender, EventArgs e)
{
short scan_result = scanner_api.Scan();
MessageBox.Show("Result = " + scan_result);
if (scan_result == 1)
parse_api.Parse(); // This will check for a saved image the scanner_api stores on disk, and then convert it.
}
It works and display the results.
Is there a way to do this, how?
Thank you very much!
UPDATE:
Theres an event on the scanner API:
Public Event EndScan() // Occurs when the scanned the image.
But I don't know how to use it. Any Idea?
That really depends on how the API works. If scanner_api.Scan() is blocking, then it will sit at that line waiting for a result. Once it gets the result, the if will evaluate. This can cause your UI to become unresponsive, so you often have to implement some sort of threading to do it in the background. I'm guessing from your question that isn't the way this API works.
Another way this could work is with polling. You check every so often to see what the result is. You don't want to check constantly and use up all your resources (such as CPU), so you check at an interval. Sheldon's answer with a Timer achieves this.
At least one more way this may work is with a callback. You send the API a callback function to call when the status has updated. This can be implemented as events (delegate) you tie into or a regular delegate you pass as a parameter. You'll often see these implemented as "OnStatusChanged", "OnCompleted", etc.
Basically, it's down to what the API supports. Polling usually works, the others have to be supported. Check your API documentation for examples if possible.
You can use a timer (see MSDN: Timer class) that periodically checks whether the scan already completed or not.
You can alternatively use an asynchronous call that calls back when the scanning process is finished. Note that this is the more complicated way.
One way would be with a timer. Set the timer to check every few seconds to check the value in scan_result (which would need to be promoted to a class-level variable for this to work).
So, something like:
public class Scanning
{
private System.Timers.Timer aTimer;
short scan_result;
public Scanning()
{
aTimer = new System.Timers.Timer(1000);
// Hook up the Elapsed event for the timer.
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
}
private void ButtonScanAndParse_Click(object sender, EventArgs e)
{
aTimer.Enabled = true;
scan_result = scanner_api.Scan();
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
if (scan_result == 1)
{
aTimer.Enabled = false;
parse_api.Parse(); // This will check for a saved image the scanner_api stores on disk, and then convert it.
}
}
}
(This is untested, of course. YMMV.)