Why is my GUI freezing? - wpf

I'm new in TPL world, and I did that code:
var myItems = myWpfDataGrid.SelectedItems;
this.Dispatcher.BeginInvoke(new Action(() =>
{
var scheduler = new LimitedConcurrencyLevelTaskScheduler(5);
TaskFactory factory = new TaskFactory(scheduler);
foreach (MyItem item in myItems)
{
Task myTask = factory.StartNew(() =>
DoLoooongWork(item)
).ContinueWith((t) =>
{
Debug.WriteLine(t.Exception.Message);
if (t.Exception.InnerException != null)
{
Debug.WriteLine(t.Exception.InnerException.Message);
}
},
TaskContinuationOptions.OnlyOnFaulted);
}
}), null);
The only one access to gui is "var myItems = myWpfDataGrid.SelectedItems;"
and it is read only! The function "DoLoooongWork()" does access to serial ports, etc. It's a separated SDK function that doesn't access the GUI. I know that "Dispatcher.BeginInvoke" is a bit redundant, but I don't know what I can do, or what I'm doing wrong. The only reason to this code is to free the GUI while "DoLoooongWork()" executes, but the GUI is frozen!
What's wrong with that code?
edit
Thanks to #Euphoric help, I discovered the problem that is similar to that post:
COM Interop hang freezes entire COM system. How to cancel COM call

I presume some objects inside DoLoooongWork require thread affinity and message pumping. Try my ThreadWithAffinityContext and see if helps, use it like this:
private async void Button_Click(object sender, EventArgs e)
{
try
{
using (var staThread = new Noseratio.ThreadAffinity.ThreadWithAffinityContext(
staThread: true, pumpMessages: true))
{
foreach (MyItem item in myItems)
{
await staThread.Run(() =>
{
DoLoooongWork(item);
}, CancellationToken.None);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
More info about ThreadWithAffinityContext.
[UPDATE] You mentioned in the comments that the code inside DoLoooongWork looks like this:
zkemkeeper.CZKEM axCZKEM1 = new zkemkeeper.CZKEM();
axCZKEM1.Connect_Net(ip, port);
I never heard of "zkemkeeper" before, but I did a brief search and found this question. Apparently, Connect_Net only establishes the connection and starts a session, while the whole communication logic happens asynchronously via some events, as that question suggests:
bIsConnected = axCZKEM1.Connect_Net("192.168.0.77", Convert.ToInt32("4370"));
if (bIsConnected == true)
{
iMachineNumber = 1;
if (axCZKEM1.RegEvent(iMachineNumber, 65535))
{
this.axCZKEM1.OnFinger += new kemkeeper._IZKEMEvents_OnFingerEventHandler(axCZKEM1_OnFinger);
this.axCZKEM1.OnVerify += new zkemkeeper._IZKEMEvents_OnVerifyEventHandler(axCZKEM1_OnVerify);
// ...
}
}
That would be a whole different story. Leave a comment if that's the case and you're still interested in some solution.

I had a hunch that something working with serial port would try to use application's event loop to do it's work. So it actually bypasses the whole dispatcher and thread system and blocks the application. I'm not experienced in this field so I don't know how to solve it, but this is different question.

Related

ChannelFactory method call increse memory

I have an winform application which consumes windows service, i user ChannelFactory
to connect to service, problem is when i call service method using channel the memory usage increase and after
method execute memory not go down(even after form close), i call GC.Collect but no change
channel Create class
public class Channel1
{
List<ChannelFactory> chanelList = new List<ChannelFactory>();
ISales salesObj;
public ISales Sales
{
get
{
if (salesObj == null)
{
ChannelFactory<ISales> saleschannel = new ChannelFactory<ISales>("SalesEndPoint");
chanelList.Add(saleschannel);
salesObj = saleschannel.CreateChannel();
}
return salesObj;
}
}
public void CloseAllChannels()
{
foreach (ChannelFactory chFac in chanelList)
{
chFac.Abort();
((IDisposable)chFac).Dispose();
}
salesObj = null;
}
}
base class
public class Base:Form
{
public Channel1 channelService = new Channel1();
public Channel1 CHANNEL
{
get
{
return channelService;
}
}
}
winform class
Form1:Base
private void btnView_Click(object sender, EventArgs e)
{
DataTable _dt = new DataTable();
try
{
gvAccounts.AutoGenerateColumns = false;
_dt = CHANNEL.Sales.GetDatatable();
gvAccounts.DataSource = _dt;
}
catch (Exception ex)
{
MessageBox.Show("Error Occurred while processing...\n" + ex.Message, "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
finally
{
CHANNEL.CloseAllChannels();
_dt.Dispose();
//GC.Collect();
}
}
You're on the right track in terms of using ChannelFactory<T>, but your implementation is a bit off.
ChannelFactory<T> creates a factory for generating channels of type T. This is a relatively expensive operation (as compared to just creating a channel from the existing factory), and is generally done once per life of the application (usually at start). You can then use that factory instance to create as many channels as your application needs.
Generally, once I've created the factory and cached it, when I need to make a call to the service I get a channel from the factory, make the call, and then close/abort the channel.
Using your posted code as a starting point, I would do something like this:
public class Channel1
{
ChannelFactory<ISales> salesChannel;
public ISales Sales
{
get
{
if (salesChannel == null)
{
salesChannel = new ChannelFactory<ISales>("SalesEndPoint");
}
return salesChannel.CreateChannel();
}
}
}
Note that I've replaced the salesObj with salesChannel (the factory). This will create the factory the first time it's called, and create a new channel from the factory every time.
Unless you have a particular requirement to do so, I wouldn't keep track of the different channels, especially if follow the open/do method/close approach.
In your form, it'd look something like this:
private void btnView_Click(object sender, EventArgs e)
{
DataTable _dt = new DataTable();
try
{
gvAccounts.AutoGenerateColumns = false;
ISales client = CHANNEL.Sales
_dt = client.GetDatatable();
gvAccounts.DataSource = _dt;
((ICommunicationObject)client).Close();
}
catch (Exception ex)
{
((ICommunicationObject)client).Abort();
MessageBox.Show("Error Occurred while processing...\n" + ex.Message, "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
The code above gets a new ISales channel from the factory in CHANNEL, executes the call, and then closes the channel. If an exception happens, the channel is aborted in the catch block.
I would avoid using Dispose() out of the box on the channels, as the implementation in the framework is flawed and will throw an error if the channel is in a faulted state. If you really want to use Dispose() and force the garbage collection, you can - but you'll have to work around the WCF dispose issue. Google will give you a number of workarounds (google WCF Using for a start).

how do i convert these c# winform codes to be compatible on c# wpf?

hi im working on a project that uses invoke and threads.. it is a simple remote desktop program with chat.. i got a sample here on the internet in c# winform, but i would like to convert it to wpf.. i have no problem in sending message to another client using the wpf program but it cannot receive ( or cannot read) the sent messages from the others.. i think it has something to do with the thread and the invoke method, i read that wpf does invoke differently and i did try the dispatcher.invoke, but it still doesnt do the trick
pls hellp
here's the code
wait = new Thread(new ThreadStart(waitForData));
wait.Start();
that snippet above is executed when a successful connection is made in tcpclient
private void waitForData()
{
try
{
NetworkStream read = tcpclnt.GetStream();
while (read.CanRead)
{
byte[] buffer = new byte[64];
read.Read(buffer, 0, buffer.Length);
s = new ASCIIEncoding().GetString(buffer);
System.Console.WriteLine("Recieved data:" + new ASCIIEncoding().GetString(buffer));
rcvMsg = new ASCIIEncoding().GetString(buffer) + "\n";
hasNewData = true;
bool f = false;
f = rcvMsg.Contains("##");
bool comand = false;
comand = rcvMsg.Contains("*+*-");
/*File receive*/
if (f)
{
string d = "##";
rcvMsg = rcvMsg.TrimStart(d.ToCharArray());
int lastLt = rcvMsg.LastIndexOf("|");
rcvMsg = rcvMsg.Substring(0, lastLt);
NetworkStream ns = tcpclnt.GetStream();
if (ns.CanWrite)
{
string dataS = "^^Y";
byte[] bf = new ASCIIEncoding().GetBytes(dataS);
ns.Write(bf, 0, bf.Length);
ns.Flush();
}
try
{
new Recieve_File().recieve_file(rcvMsg);
}
catch (Exception ec)
{
System.Console.WriteLine(ec.Message);
}
}
/*Command-shutdown/restart/logoff*/
else if (comand)
{
string com = "*+*-";
rcvMsg = rcvMsg.TrimStart(com.ToCharArray());
execute_command(rcvMsg);
}
else
{
this.Invoke(new setOutput(setOut));
Thread.Sleep(1000);
}
}
}
catch (Exception ex)
{
wait.Abort();
output.Text += "Error..... " + ex.StackTrace;
}
}
the snippet above is a code that listens if there is a message or command.. the line
this.invoke(new setoutput(setout)) is a code for appending text in the rtb
hope someone could help me thanks
You've posted a lot of code, but I'm assuming it's only the call to Control.Invoke which is causing the problem. In WPF, use Dispatcher.Invoke (or Dispatcher.BeginInvoke) instead, via the Dispatcher property on the relevant UI element.
I'd also strongly encourage you to:
Refactor your code into smaller methods
Stop catching just Exception except at the top level of any large operation (it should just be a fall-back; usually you catch specific exceptions)
Start following .NET naming conventions
Add a using directive for System so you can just write Console.WriteLine instead of System.Console.WriteLine everywhere
Use Encoding.ASCII instead of creating a new ASCIIEncoding each time you need one
Use a StreamReader to read character data from a stream, instead of reading it as binary data first and then encoding it
For either Stream or TextReader, don't ignore the return value from Read - it tells you how many bytes or characters have been read

Silverlight Asynchronous operation Foreach loop

I have a query for getting a list of prescriptions as below:
var PRSCRPTSQuery = GV.dbContext.Load(GV.dbContext.GetPRSCRPTQuery(GV.curCustomer.CustCode,
oOrdritemEdited.ProdCode, oOrdritemEdited.MedCode));
PRSCRPTSQuery.Completed += new EventHandler(PRSCRPTSQuery_Completed);
In the query completed event, I have the following code :
void PRSCRPTSQuery_Completed(object sender, EventArgs e)
{
lstPRSCRPT = GV.dbContext.PRSCRPTs.Where(p=>p.Status =="Activated").ToList();
if (lstPRSCRPT.Count > 0)
{
foreach (var rec in lstPRSCRPT)
{
var OrderItemQuery = GV.dbContext.Load(GV.dbContext.GetOrdritemsQuery(rec.PresNo));
OrderItemQuery.Completed += new EventHandler(OrderItemQuery_Completed);
}
}
}
The list lstPRSCRPT can contain more than one record. I presume, the foreach loop will advance to the next item in the loop without waiting for the OrderItemQuery_Completed event which is below:
void OrderItemQuery_Completed(object sender, EventArgs e)
{
lstOrderItem = GV.dbContext.OrderItems.ToList();
if (lstOrderItem.Count > 0)
{
foreach (var OrdrItemRec in lstOrderItem)
{
TotTonnes = (double)(TotTonnes + OrdrItemRec.Quantity);
}
}
}
Is there any work around for this situation? I am new to the asynchronous type of programming in SL
I see where your coming from and when i first started Silverlight programming i gripped to my preconceptions of synchronous execution so i know what i have when ive finished calling a query and also i know exactly where it's errored.
Silverlight however takes this concept and tries to rip it from you yelling "This way is better trust me!" and for the purpose it serves of enriching client side interactivity it certainly succeeds. It just takes time. You just need to learn more about the style of how to link it all together.
The link previously shown by Faster Solutions shows where C# is going in terms of asynchronous coding but it pays to know what its actually accomplishing for you. Some of which you've already grasped in the code you've linked in the question.
When i've faced the same situation you have where you have back to back async callbacks is to raise an event when i've finished doing what i'm doing. For example:
public event EventHandler<EventArgs> LoadComplete;
public int QueryCount {get;set;}
public int QuerysCompleted {get;set;}
public void GetItems()
{
var PRSCRPTSQuery = GV.dbContext.Load(GV.dbContext.GetPRSCRPTQuery
(GV.curCustomer.CustCode, oOrdritemEdited.ProdCode, oOrdritemEdited.MedCode));
PRSCRPTSQuery.Completed += new EventHandler(PRSCRPTSQuery_Completed);
LoadComplete += loader_LoadComplete;
}
void PRSCRPTSQuery_Completed(object sender, EventArgs e)
{
lstPRSCRPT = GV.dbContext.PRSCRPTs.Where(p=>p.Status =="Activated").ToList();
if (lstPRSCRPT.Count > 0)
{
QueryCount = lstPRSCRPT.Count;
foreach (var rec in lstPRSCRPT)
{
var OrderItemQuery = GV.dbContext.Load(GV.dbContext.GetOrdritemsQuery(rec.PresNo));
OrderItemQuery.Completed += new EventHandler(OrderItemQuery_Completed);
}
}
}
void OrderItemQuery_Completed(object sender, EventArgs e)
{
QueryCompleted++;
lstOrderItem = GV.dbContext.OrderItems.ToList();
if (lstOrderItem.Count > 0)
{
foreach (var OrdrItemRec in lstOrderItem)
{
TotTonnes = (double)(TotTonnes + OrdrItemRec.Quantity);
}
}
if(QueryCompleted == QueryCount)
{
RaiseLoadComplete();
}
}
public void RaiseLoadComplete()
{
if(LoadComplete != null)
{
LoadComplete(this, new EventArgs());
}
}
void loader_LoadComplete(object sender, EventArgs e)
{
//Code to execute here
}
I attach an event when starting the first query of what code to execute when i'm done. In the first query call back i initialise a count of how many responses i am expecting. Then in the second query callback i increment until i get the right amount and call the event to say im done.
The only caution with this approach is if one of the queries error, The final code will never get executed.
You might find that the VS Async CTP of interest. It introduces the new "async" keyword for handling asyncronous events. He's a blog explaining it: VS Async

WPF, calling server method in background worker

I need in wpf app check messages on server. I have own method which load messages on server-LoadRp().
I would like to create some kind of listener which would check, every 3 seconds whether on the server are not new messages.
I call method for loading messages on dispatcher timer tick event, it is suitable? Any another solution. It’s possible call timer in another thread in wpf?
Code is here:
public MessangerWindow(PokecCommands pokecCmd)
{
InitializeComponent();
PokecCmd = pokecCmd;
_friendsData = PokecCmd.LoadFriends();
friendsListBox.DataContext = _friendsData;
_dispatcherTimer = new DispatcherTimer();
_dispatcherTimer.Tick+=new EventHandler(DispatcherTimer_Tick);
_dispatcherTimer.Interval = new TimeSpan(0,0,3);
_dispatcherTimer.Start();
}
private void DispatcherTimer_Tick(object sender, EventArgs eventArgs)
{
try
{
//try load new message from sever
RP message = PokecCmd.LoadRp();
//arived message
if (message != null)
{
//exist window
if (_chatWindows.ContainsKey(message.Nick))
{
_chatWindows[message.Nick].Show();
}
{
//create new Window
var chatWindow = new ChatWindow(PokecCmd, message);
_chatWindows.Add(message.Nick, chatWindow);
chatWindow.Show();
}
}
}
catch (Exception ex)
{
//MessageBox.Show(ex.Message);
}
}
What is suitable to use:
Dispatcher with no background threads
Dispatcher with background threads
Multiple Threads
If you are ok with locking up your UI for the time it takes to check on the server, using a DispatcherTimer the way you are doing it will work fine.
If checking for new messages could take more than a few milliseconds and you want your UI to be responsive while it checks, you should use multiple threads. In that case, once the new data had arrived you would use Dispatcher.Invoke to display it.
Your code in the thread that checks for messages might look like this:
//try load new message from sever
RP message = PokecCmd.LoadRp();
//arived message
if( message != null )
Dispatcher.Invoke(DispatcherPriority.Send, new Action(() =>
{
//exist window
if (_chatWindows.ContainsKey(message.Nick))
{
_chatWindows[message.Nick].Show();
}
{
//create new Window
var chatWindow = new ChatWindow(PokecCmd, message);
_chatWindows.Add(message.Nick, chatWindow);
chatWindow.Show();
}
}
);

How to run batched WCF service calls in Silverlight BackgroundWorker

Is there any existing plumbing to run WCF calls in batches in a BackgroundWorker?
Obviously since all Silverlight WCF calls are async - if I run them all in a backgroundworker they will all return instantly.
I just don't want to implement a nasty hack if theres a nice way to run service calls and collect the results.
Doesnt matter what order they are done in
All operations are independent
I'd like to have no more than 5 items running at once
Edit: i've also noticed (when using Fiddler) that no more than about 7 calls are able to be sent at any one time. Even when running out-of-browser this limit applies. Is this due to my default browser settings - or configurable also. obviously its a poor man's solution (and not suitable for what i want) but something I'll probably need to take account of to make sure the rest of my app remains responsive if i'm running this as a background task and don't want it using up all my connections.
I think your best bet would be to have your main thread put service request items into a Queue that is shared with a BackgroundWorker thread. The BackgroundWorker can then read from the Queue, and when it detects a new item, initiate the async WCF service request, and setup to handle the AsyncCompletion event. Don't forget to lock the Queue before you call Enqueue() or Dequeue() from different threads.
Here is some code that suggests the beginning of a solution:
using System;
using System.Collections.Generic;
using System.ComponentModel;
namespace MyApplication
{
public class RequestItem
{
public string RequestItemData { get; set; }
}
public class ServiceHelper
{
private BackgroundWorker _Worker = new BackgroundWorker();
private Queue<RequestItem> _Queue = new Queue<RequestItem>();
private List<RequestItem> _ActiveRequests = new List<RequestItem>();
private const int _MaxRequests = 3;
public ServiceHelper()
{
_Worker.DoWork += DoWork;
_Worker.RunWorkerAsync();
}
private void DoWork(object sender, DoWorkEventArgs e)
{
while (!_Worker.CancellationPending)
{
// TBD: Add a N millisecond timer here
// so we are not constantly checking the Queue
// Don't bother checking the queue
// if we already have MaxRequests in process
int _NumRequests = 0;
lock (_ActiveRequests)
{
_NumRequests = _ActiveRequests.Count;
}
if (_NumRequests >= _MaxRequests)
continue;
// Check the queue for new request items
RequestItem item = null;
lock (_Queue)
{
RequestItem item = _Queue.Dequeue();
}
if (item == null)
continue;
// We found a new request item!
lock (_ActiveRequests)
{
_ActiveRequests.Add(item);
}
// TBD: Initiate an async service request,
// something like the following:
try
{
MyServiceRequestClient proxy = new MyServiceRequestClient();
proxy.RequestCompleted += OnRequestCompleted;
proxy.RequestAsync(item);
}
catch (Exception ex)
{
}
}
}
private void OnRequestCompleted(object sender, RequestCompletedEventArgs e)
{
try
{
if (e.Error != null || e.Cancelled)
return;
RequestItem item = e.Result;
lock (_ActiveRequests)
{
_ActiveRequests.Remove(item);
}
}
catch (Exception ex)
{
}
}
public void AddRequest(RequestItem item)
{
lock (_Queue)
{
_Queue.Enqueue(item);
}
}
}
}
Let me know if I can offer more help.

Resources