|
I have no idea - I've never tried, and I'm really not sure how I would test it ...
I would doubt it: if all the DataReceived events are set up by the same thread, then the code to kick the actual event off will all be executing on that thread (though the handler itself will be executed on a new thread / pool thread) so there will be some delay introduced there. I also suspect that the actual serial port char received interrupt goes through many layers of software before it even gets to the SerialPort class and then be routed to the right thread to kick of the Event.
When I need multiple sources, I prefer to use separate threads and poll instead of use Events as it's rather more "immediate".
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
OriginalGriff wrote: I would doubt it: if all the DataReceived events are set up by the same thread No, I wouldn't do that, I would create my SerialPort objects and hook up the DataReceived events in separate threads.
modified 30-Jan-21 8:10am.
|
|
|
|
|
That's just adding a further layer of abstraction - the DataReceived event is fired on a pool thread, not the original starter thread, so ...
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
So you're saying it's not a good idea? How many pool threads does a PC have? Can I somehow dictate what pool thread the different Com ports should issue events onto?
|
|
|
|
|
arnold_w wrote: How many pool threads does a PC have? Depends: Thread Pools - Win32 apps | Microsoft Docs[^]
arnold_w wrote: Can I somehow dictate what pool thread the different Com ports should issue events onto? No. It's a pool. As an when a thread is needed, one is pulled from the pool and started.
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
I need to peel potatoes but I don't want to use a potato peeler.
It was only in wine that he laid down no limit for himself, but he did not allow himself to be confused by it.
― Confucian Analects: Rules of Confucius about his food
|
|
|
|
|
You haven't got a swiss army knife?
Software rusts. Simon Stephenson, ca 1994. So does this signature. me, 2012
|
|
|
|
|
I do! ... But I don't want to use it because it might do the job.
It was only in wine that he laid down no limit for himself, but he did not allow himself to be confused by it.
― Confucian Analects: Rules of Confucius about his food
|
|
|
|
|
A Serialport as a part of the UI - so Threading isn't a good idea. But you can also work with the DataReceived-Event from the/each port. Why do you want to use Threading or a Backgroundworker for this approach ?
Do you have considered that there are other Control than a Textbox ? For example a ListView ... But you should realize that each and every list has an End ... so an endless Form isn't really possible to realize.
If you want to write to an Excel-Sheet it isn't necessary to know something about VBA - this work could be done complete inside your Application.
What problem do you have with a DataGridView ? But I think it isn't the right approach (! glass-bowl )
RichTextBox could also be a good idea ...
|
|
|
|
|
SerialPort isn't a UI control: it can be dropped on a form if you must, but it doesn't have any display component (that's why it appears at the bottom, off the form like a Timer control does).
You can - and should - access it from a thread other than the UI: indeed the DataReceived event is never fired on the UI thread anyway!
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Ralf Meier wrote: Why do you want to use Threading or a Backgroundworker for this approach ? Let's assume updating the GUI component with an extra row takes 10 ms, but queueing a task takes 1 ms. Then, if I don't queue the task, the event handler will be blocked for 10 ms for each packet, but if I queue a task instead then my event handler will only block for 1 ms. Of course, with the latter approach my data will show up with a slight delay in the GUI component, but that's acceptable.Ralf Meier wrote: For example a ListView I've never used a ListView. Would it be a better choice than RichTextBox and DataGridView, in your opinion?Ralf Meier wrote: But you should realize that each and every list has an End That's fine, I don't have infinite RAM in my computer anyways, but for sure 65535 characters is too little. Ralf Meier wrote: If you want to write to an Excel-Sheet it isn't necessary to know something about VBA - this work could be done complete inside your Application. But how do I know which row I should add the latest data to, without running a for-loop to find the first empty row? RichTextBox and DataGridBox have append methods that will figure out where the end is for you.Ralf Meier wrote: What problem do you have with a DataGridView ? Today I have a logging window (that supports a single Com port) implemented as DataGridview and it seems to be lagging a lot, but I guess I must be doing something wrong. Also, I had to implement my own copy-to-clipboard function and I had some problems with it because every once in a while (e.g. during boot-up and power down) there would come strange characters from my device and if a null-character was present in the middle, then only half the text would appear in the clipboard, so I had to spend time making a workaround for that. With e.g. RichTextBox, I wouldn't have to do this.
modified 30-Jan-21 7:04am.
|
|
|
|
|
Hi,
here are my suggestions:
1.
use a single BackGroundWorker per COM port; use its progress reporting facility to report displayable results.
2.
Use synchronous read operations, thus avoiding the tricky DataReceived event.
If you are lucky and the peripheral reports text messages ending on some specific terminator, use ReadLine(); when necessary, adjust the NewLine property to equal that terminator. If not, just use Read and have the BGW discover the beginning and end of every message.
3.
For the UI use the simplest List oriented Control that satisfies your interactive requirements; a ListView is good, however I prefer a ListBox.
4.
Define a little class holding the raw data of one displayable result. That is one object, no more expensive than the array a ListView would need. Instances of this class, not strings, are to be added to the ListBox Items collection.
5.
Make the ListBox ownerdrawn, giving it a fixed ItemHeight, and an appropriate DrawItem handler that takes care of horizontal positioning, coloring, and whatever fancy styling you may want.
That's it; and it is bound to be simpler than the code you probably are experementing with right now...
Luc Pattyn [My Articles]
If you can't find it on YouTube try TikTok...
|
|
|
|
|
I want to call SaveChanges in my WPF app. SaveChanges calls AddCompany or UpdateCompany on the server, I DON'T want the UI to hang up. After the save completes, there are other things I need to do.
public override bool SaveChanges()
{
bool isValid = Validate();
if (isValid)
{
var task = Task.Factory.StartNew(() =>
{
if (Company.Id == 0)
{
AppCore.BizObject.AddCompany(Company);
}
else
{
AppCore.BizObject.UpdateCompany(Company);
}
}).ContinueWith(a =>
{
});
}
return isValid;
}
This is being called from a Save button. This doesn't feel right. What's the right way to call AddCompany/UpdateCompany asynchronously and then handle the clean up stuff? I'm not sure how to structure the code.
Thanks
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
modified 27-Jan-21 12:49pm.
|
|
|
|
|
Move it to a second thread: I'd suggest a BackgroundWorker as a simple to manage way to do it - it can report progress and completion via Events
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Doesn't that negate the whole purpose of Async/Await?
What I'm trying to understand is the correct way to use Async/Await.
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
As already mentioned by OriginalGriff the correct way would be using a worker thread
modified 28-Jan-21 4:34am.
|
|
|
|
|
For such an aggressive use of bold there, it's amusing that you called out the wrong person. Kevin Marois is the person who asked the question.
|
|
|
|
|
Sorry! the fast copy/paste operations may not always be 100% correct!
PS: I have edited my post.
|
|
|
|
|
I'm with you. BGW is underappreciated and often misunderstood; particularly the "reporting" part (object state versus the pseudo "% complete").
It was only in wine that he laid down no limit for himself, but he did not allow himself to be confused by it.
― Confucian Analects: Rules of Confucius about his food
|
|
|
|
|
The method you're overriding doesn't return a Task (or ValueTask ), so you can't make the override an async method.
You would need to make the method in the base class return either Task<bool> or ValueTask<bool> . The calling method would then need to wait for the returned task to complete before proceeding, either using await or ContinueWith .
It's also not helped by the fact that the methods you want to call on a background thread are synchronous methods. It would be better if they were async as well, so you could avoid explicitly spinning up a new Task to run them off-UI.
public override ValueTask<bool> SaveChanges()
{
bool isValid = Validate();
if (isValid)
{
var task = Company.Id == 0
? Task.Run(() => AppCore.BizObject.AddCompany(Company))
: Task.Run(() => AppCore.BizObject.UpdateCompany(Company));
await task;
}
return isValid;
} Understanding the Whys, Whats, and Whens of ValueTask | .NET Blog[^]
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Thank you!
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
Ok, so I've always been confused by the Client/Server design in an async app. Is this the right way to make a server method async?
Server Side
public interface IServerAPI
{
Task AddCompanyAsync(CompanyEntity entity);
Task UpdateCompanyAsync(CompanyEntity entity);
}
public class ServerAPI : IServerAPI
{
public async Task AddCompanyAsync(CompanyEntity entity)
{
var t = await Task.Factory.StartNew(() =>
{
return 1;
});
return t;
}
public async Task UpdateCompanyAsync(CompanyEntity entity)
{
await Task.Factory.StartNew(() =>
{
});
}
}
WPF Client
Some code ommitted. As you saw, the Save command is in a base class, so I can't replace it with an AsyncCommand. Instead I have it calling an async SaveChanges method:
public partial class MainWindow : Window, INotifyPropertyChanged
{
#region Base Class Abstract Code
private ICommand _SaveCommand;
public ICommand SaveCommand
{
get
{
if (_SaveCommand == null)
_SaveCommand = new RelayCommand(p => SaveExecuted(), p => SaveCanExecute());
return _SaveCommand;
}
}
private bool SaveCanExecute()
{
return true;
}
private async void SaveExecuted()
{
try
{
await SaveChanges();
}
catch (Exception e)
{
throw;
}
finally
{
}
}
#endregion Base Class Abstract Code
private async Task SaveChanges()
{
IServerAPI serverAPI = new ServerAPI();
if (Company.Id == 0)
{
var newId = await serverAPI.AddCompanyAsync(Company);
Company.Id = newId;
}
else
{
await serverAPI.UpdateCompanyAsync(Company);
}
}
}
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
Not really. Spinning up a new Task and awaiting it is only really useful if you want to move the processing onto a background thread. On the server, that doesn't make any sense.
On the server-side, you'd want to have async calls to the database. Entity Framework offers various async methods. Or, if you're using raw ADO.NET, use DbConnection.OpenAsync[^], DbCommand.ExecuteReaderAsync[^], etc.
On the client, you want the network call to be async , so that your code isn't blocked waiting for the network request to complete. If you're using HttpClient or RestClient , the default methods for calling the server will be async . If you're using WCF or HttpWebRequest , you might have to jump through a few hoops to make a nice async method.
You should generally avoid async void methods:
Avoid async void methods | You’ve Been Haacked[^]
Instead, follow David Fowler's advice[^] and store the Task returned from your async method in a discard variable:
private void SaveExecuted()
{
_ = SaveAsync();
}
private async Task SaveAsync()
{
try
{
IServerAPI serverAPI = new ServerAPI();
if (Company.Id == 0)
{
Company.Id = await serverAPI.AddCompanyAsync(Company);
}
else
{
await serverAPI.UpdateCompanyAsync(Company);
}
}
finally
{
}
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Richard Deeming wrote: Not really. Spinning up a new Task and awaiting it is only really useful if you want to move the processing onto a background thread. On the server, that doesn't make any sense.
OK, so what would the server methood look like? I'm a bit confused
In that Fowler article, he has this example:
public async Task<int> DoSomethingAsync()
{
var result = await CallDependencyAsync();
return result + 1;
}
I thought that the server method had to return a task to make it Async?
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
modified 29-Jan-21 18:12pm.
|
|
|
|
|
The server method can return a Task , if it's doing work that can be executed asynchronously. For example, talking to a database, calling another API, or accessing the file system.
Whether or not the server method returns a Task , the calling client can use a Task to call the server. The network request will be the asynchronous part of the call; the client doesn't care how the server handles the request.
Eg:
Server:
public int Foo()
{
return 42;
}
public async Task<int> BarAsync()
{
using (var connection = new SqlConnection("..."))
using (var command = new SqlCommand("...", connection))
{
await connection.OpenAsync();
object result = await command.ExecuteScalarAsync();
return Convert.ToInt32(result);
}
} Client:
private readonly HttpClient _client;
public async Task<int> FooAsync()
{
using (var response = await _client.GetAsync("/api/foo"))
{
response.EnsureSuccessStatusCode();
return await response.ReadAsAsync<int>();
}
}
public async Task<int> BarAsync()
{
using (var response = await _client.GetAsync("/api/bar"))
{
response.EnsureSuccessStatusCode();
return await response.ReadAsAsync<int>();
}
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|