I am trying to update the TextBox and ListView simultaneously. However, I cannot. The Listview is updating, but the TextBox is only updating sometimes.
private void updateGUI(ISynchronizeInvoke asyncInvoke, Action action)
{
try
{
if (!asyncInvoke.InvokeRequired)
action();
else
asyncInvoke.BeginInvoke(action, new object[] { });
}
catch (Exception ex)
{
Console.WriteLine("update gui error: " + ex.Message);
}
}
// CallBack for update GUI
private void onProgressChange(object source, DownloadEventArgs e)
{
// Update ListView and TextBox
this.updateGUI
(
this.lstDownloads,
() =>
{
this.downloadTotalSize += e.currentDlBytes;
this.lstDownloads.Items[e.downloadIndex].SubItems[1].Text = e.downloadBytes;
this.lstDownloads.Items[e.downloadIndex].SubItems[2].Text = e.downloadProgress + "%";
this.lblDownloadSize.Text = e.downloadBytes + " / " + this.downloadTotalSize
}
);
}
Can someone explain what I'm doing wrong? Sorry for typos. Regards.
Related
I have an async function which still freezes / lags the UI thread for me when I execute it. This is my function calling it.
private void TcpListenerLogic(object sender, string e)
{
Application.Current.Dispatcher.BeginInvoke((Action)async delegate {
try
{
dynamic results = JsonConvert.DeserializeObject<dynamic>(e);
if (results.test_id != null)
{
// Get properties for new anchor
string testInformation = await CommunicationCommands.getJsonFromURL(
"http://" + ServerIP + ":" + ServerPort + "/api/" + results.test_id);
}
}
catch (Exception exception)
{
// Writing some Trace.WriteLine()'s
}
});
}
And this is the async function that freezes my UI Thread
public static async Task<string> getJsonFromURL(string url)
{
try
{
string returnString = null;
using (System.Net.WebClient client = new System.Net.WebClient())
{
returnString = await client.DownloadStringTaskAsync(url);
}
return returnString;
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
return null;
}
}
I already tried to make everything in TcpListenerLogic run in a new Thread:
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
}).Start();
Which resulted in the whole UI completely freezing. And I tried to make TcpListenerLogic async and await the dispatcher, which also made everything freeze permanently. I also tried to make TcpListenerLogic async and leave the dispatcher. The dispatcher is only there because I normally have some UI code in there, which I left out for my tests.
I have ventured far through the internet, but no BackgroundWorker, ThreadPool or other methods helped me in my endeavour.
If anyone has help for this particular problem, or a resource that would improve my understanding of async functions in C#, I would much appreciate it.
Edit
As requested a deeper insight in how this event handler is called.
I have System.Net.Websocket, which is connected to the Backend API I am working with and triggers an event, everytime he receives new Data. To guarantee the socket listens as longs as it is open, there is a while loop which checks for the client state:
public event EventHandler<string> TcpReceived;
public async void StartListener(string ip, int port, string path)
{
try
{
using (client = new ClientWebSocket())
{
try
{ // Connect to backend
Uri serverUri = new Uri("ws://" + ip + ":" + port.ToString() + path );
await client.ConnectAsync(serverUri, CancellationToken.None);
}
catch (Exception ex)
{
BackendSettings.IsConnected = false;
Debug.WriteLine("Error connecting TCP Socket: " + ex.ToString());
}
state = client.State;
// Grab packages send in backend
while (client.State == WebSocketState.Open || client.State == WebSocketState.CloseSent)
{
try
{
// **Just formatting the received data until here and writing it into the "message" variable**//
TcpReceived(this, message);
// Close connection on command
if (result.MessageType == WebSocketMessageType.Close)
{
Debug.WriteLine("Closing TCP Socket.");
shouldstayclosed = true;
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
break;
}
state = client.State;
}
catch
{
BackendSettings.IsConnected = false;
state = client.State;
}
}
state = client.State;
}
}
catch (Exception ex)
{
// Some error messages and settings handling
}
}
The Event has a handler attached:
TcpReceived += TcpListener_TcpReceived;
And this is the Handler, which calls the previously seen "TcpListenereLogic".
private void TcpListener_TcpReceived(object sender, string e)
{
TcpListenerLogic(sender, e);
//App.Current.Dispatcher.BeginInvoke(new Action(() => {
// TcpListenerLogic(sender, e);
//}));
//new Thread(() =>
//{
// Thread.CurrentThread.IsBackground = true;
// TcpListenerLogic(sender, e);
//}).Start();
}
I previously had the "TcpListenereLogic" as the handler, but I wanted to try different methods to call it. I also left in the commented out part, to show how the call of "TcpListenereLogic" looked already. All my attempts were with all mentioned setups and sadly lead to nothing.
Thank you very much #TheodorZoulias for helping me to find the solution to my problem.
It turns out it wasn't the async function itself, but rather how often it gets called. It got called roughly ~120 times every second.
My solution starts by calling the Listener method over a new Thread:
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
MainWindow.tcpListener.StartListener(ip, portNumber, "/api/");
}).Start();
To limit the amount of calls that happen every second I added a dispatcher timer, that resets a bool after it has been used for a call, by my Event.
readonly System.Windows.Threading.DispatcherTimer packageIntervallTimer =
new System.Windows.Threading.DispatcherTimer();
bool readyForNewPackage = true;
private void ReadyForPackage(object sender, EventArgs e)
{
readyForNewPackage = true;
}
public async void StartListener(string ip, int port, string path)
{
packageIntervallTimer.Interval = TimeSpan.FromMilliseconds(50);
packageIntervallTimer.Tick += (s, e) => { Task.Run(() => ReadyForPackage(s, e)); };
packageIntervallTimer.Start();
Then I wrapped everything inside the while loop into an if condition based on the bool, the most important part was to have my "event EventHandler TcpReceived" in there:
// Grab packages sent in backend
while (client.State == WebSocketState.Open || client.State == WebSocketState.CloseSent)
{
if (readyForNewPackage == true)
{
readyForNewPackage = false;
try
{
....
TcpReceived(this, message);
....
}
catch
{
...
}
}
}
I added my TcpListenerLogic to the Eventhandler:
TcpReceived += TcpListenerLogic;
And my TcpListenerLogic now looked like this (names have been changed):
private async void TcpListenerLogic(object sender, string e)
{
try
{
dynamic results = JsonConvert.DeserializeObject<dynamic>(e);
if (results.test_id != null)
{
string testID = "";
if (results.test_id is JValue jValueTestId)
{
testID = jValueTestId.Value.ToString();
}
else if (results.test_id is string)
{
testID = results.test_id;
}
// Get properties for new object
string information = await CommunicationCommands.getJsonFromURL(
"http://" + ServerIP + ":" + ServerPort + "/api/" + testID );
if (information != null)
{
await App.Current.Dispatcher.BeginInvoke(new Action(() =>
{
// Create object out of the json string
TestStatus testStatus = new TestStatus();
testStatus.Deserialize(information);
if (CommunicationCommands.isNameAlreadyInCollection(testStatus.name) == false)
{
// Add new object to the list
CommunicationCommands.allFoundTests.Add(testStatus);
}
}));
{
}
catch (Exception exception)
{
....
}
}
Adding a new Thread to execute any step results in problems, so keep in mind that all this uses the thread created at the beginning for "StartListener"
I am unable to get the reason for this Exception:
private void bwWorker_DoWork(object sender, DoWorkEventArgs e)
{
if (Main.bolDebugMode)
MessageBox.Show("Function DoWork is called");
if (Main.ftpsync(Main.strUsername407, Main.strPassword407, sender as BackgroundWorker) == 0)
e.Result = e.Result + "No error in " + Main.strUsername407;
else
{
if (Main.bolDebugMode)
MessageBox.Show("Errors in " + Main.strUsername407);
e.Cancel = true;
e.Result = e.Result + "Errors in " + Main.strUsername407;
if (Main.bolDebugMode)
MessageBox.Show("Errors marked");
try
{
MessageBox.Show("Next step throws exception");
return;
}
catch (Exception error)
{
if (error.ToString() != null)
MessageBox.Show(error.InnerException.Message);
}
}
}
It throws this exception:An unhandled exception of type 'System.Reflection.TargetInvocationException' occurred in mscorlib.dll
Additional information: Exception has been thrown by the target of an invocation.
The target (to my limited understanding) is the backgroundworker's RunWorkerCompleted function:
private void bwWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (Main.bolDebugMode)
MessageBox.Show("DoWork Completed. Break: " + e.Cancelled + " Error: " + e.Error + " Result: " + e.Result);
// First, handle the case where an exception was thrown.
if (e.Error != null)
{
lStatus.Text = e.Error.Message;
}
else if (e.Cancelled)
lStatus.Text = "Cancelled: " + e.Result.ToString();
}
else
{
lStatus.Text = "Done! " + e.Result;
Thread.Sleep(Convert.ToInt16(Main.strGlobalWaitTime));
pbProgress.Value = 0;
lStatus.Text = "";
}
if (Main.bolDebugMode)
MessageBox.Show("Analysis completed");
// Enable the Start button.
btnStart.Enabled = true;
// Disable the Cancel button.
btnCancel.Enabled = false;
}
public class Main
{
#region Variables
// Variables - FTP Settings
// Reading tons of variables from a appconfig file like so:
private static string StrGlobalWaitTime = ConfigurationManager.AppSettings["program_globalWaitTime"];
private static bool BolDeleteRemoteFiles = Convert.ToBoolean(ConfigurationManager.AppSettings["program_deleteremotefiles"]);
// Configuring the variables to receive and write
public static string strGlobalWaitTime
{
get { return StrGlobalWaitTime; }
set { StrGlobalWaitTime = value; }
}
#endregion
#region Main function
public static int ftpsync(string strUsername, string strPassword, BackgroundWorker bwWorker)
{
if (Directory.EnumerateFiles(strWorkDirectory, "*.pdf").Any())
{
bwWorker.ReportProgress(0, "Files left! Upload not complete");
Thread.Sleep(Convert.ToInt16(Main.strGlobalWaitTime));
return 1;
}
However, it doesn't even reach the first debugging message box. Thus it must be happening between the return and the beginning of the function. Is he background worker not handing over directly to the RunWorkerCompleted function? Can anyone tell me what I am missing or doing wrong?
This is my first question. I will try to provide as much information as possible. Google searches for the most obvious queries have been done.
The thing to look for whenever you encounter a TargetInvocationException is the InnerException property, which will have the "real" exception.
From the look of it, it will most likely have something to do with trying to access the GUI from inside the worker's thread. Remember that when using the DoWork handler, the code executes in a different thread from the main UI's. Therefore, calls to GUI components must be either done via Invoke calls or avoided all together.
I am coding a Forex Trading robot, and I am running an outOfMemory exception after some time (around 2 hours) using BlockingCollection.
I basically have 1 queue pair Trade chart, that are added into a dict:
private Dictionary<string, BlockingCollection<tick>> tickQueues = new Dictionary<string, BlockingCollection<tick>>();
I check the memory dump after one hour, and I can see the following items are piling up:
Count Size(bytes) Inclusive Size
ThreadPoolWorkQueue+QueueSegment 22,951 24,236,256 40,316,868
QueueUserWorkItemCallback 689,838 13,796,760 16,081,272
TimerQueueTimer 11,160 713,772 2,355,736
I have a timer that is responsible to add data to the Queue:
private void TickTimer_tick(object source, ElapsedEventArgs e) {
if (Monitor.TryEnter(LockTimerTick, GlobalSettings.APISleepDelayMSTick)) {
updateLockFailCount = 0;
try {
tick t = new tick(DateTime.Now, d.bid, d.ask);
lastBid = d.bid;
lastAsk = d.ask;
t.pair = Inst.pair;
//myTickQueue.TryAdd(t);
if (!myTickQueue.TryAdd(t)) {
functions.Logger.log("Error when adding Tick on Queue for " + Inst.pair+ " Maybe Queue is full", "SHMAPI", LOGLEVEL.WARN);
}
} catch (Exception E) {
functions.Logger.log("Error happened when refreshing tick data: " + E.Message, "SHMAPI", LOGLEVEL.ERROR);
} finally {
Monitor.Exit(LockTimerTick);
}
} else {
updateLockFailCount++;
int sev = LOGLEVEL.TRACE;
if (updateLockFailCount == 10) { sev = LOGLEVEL.DEBUG; }
if (updateLockFailCount==50) { sev = LOGLEVEL.WARN; }
if (updateLockFailCount % 100 == 0 && updateLockFailCount>=100) { sev = LOGLEVEL.ERROR; }
functions.Logger.log("Could not get lock to refresh tick data for symbol "+Symbol, "SHMAPI", sev);
}
}
And finally, my task that checks the Q:
public void startQueueTask(string Pair) {
if (!tickQueues.ContainsKey(Pair.ToUpper())) {
tickQueues.Add(Pair.ToUpper(), new BlockingCollection<tick>(GlobalSettings.tickQueueSize));
if (!MTAPIs.ContainsKey(Pair.ToUpper())) {
throw new Exception("API for pair " + Pair + " Should be initialized !!");
}
MTAPIs[Pair.ToUpper()].setTickQueue(tickQueues[Pair.ToUpper()]);
functions.Logger.log("Starting " + Pair + " Queue Task", "TICKPROCESSING", LOGLEVEL.DEBUG);
Task.Run(() => {
foreach (tick tick in tickQueues[Pair.ToUpper()].GetConsumingEnumerable()) {
try {
onTick(tick);
} catch (Exception E) {
functions.Logger.log("Error processing tick for symbol " + tick.pair + " " + E.Message, "TICKPROCESSING", LOGLEVEL.ERROR);
functions.printException(E);
}
}
functions.Logger.log("Exiting Queue Task", "TICKPROCESSING", LOGLEVEL.ERROR);
});
} else {
functions.Logger.log("Skipping " + Pair + " Queue Task because already exists", "TICKPROCESSING", LOGLEVEL.DEBUG);
}
}
I am not really sure why I am getting OOM, but it looks similar to:
http://blogs.microsoft.co.il/bnaya/2012/02/26/real-life-story-blocking-collection/
But I am not using parallel here... My Queues are empty though since week end market is closed.
Would using another timer with TryDequeue a better approach ?
Any advice would be welcome !
I switched timer to manual like this:
private void TickTimer_tick(object source, ElapsedEventArgs e) {
try {
//...
} finally {
TickTimer.Start();
}
}
And it seemed to have resolved my issue.
I am also making sure to send ticks in the Q, and they are filtered by duplicates by the Receiver, just so the Queing thread is never pending for too long as well.
Thanks for the pointer !
I am creating an windows based application which download the data from server.
I am using background thread which is created on different class to perform these download operation.And I want to continuously show the download status on rich textbox i.e on main thread.But i am unable to do this,get an Cross-thread operation not valid.
Please help me to resolve this problem.
method on Form1.cs
public void UpdateRichText(string Text)
{
SetRichText(Text);
}
public delegate void SetRichTextTextDelegate(string text);
public void SetRichText(object number)
{
if (InvokeRequired)
{
this.BeginInvoke(new SetRichTextTextDelegate(SetRichText),text);
return;
}
richTextBox1.Text += number.ToString() + "\n";
}
private void button3_Click_1(object sender, EventArgs e)
{
demo d = new demo();
d.display();
}
methods on demo.cs
Form1 f = new Form1();
public void display()
{
Thread t = new Thread(new ThreadStart(call));
t.Start();
}
public void call()
{
//when i call this method every time if(InvokeRequired) is false.
f.UpdateRichText("Called from Thread");
}
Try changing your check to:
if (richTextBox1.InvokeRequired)
{
richTextBox1.BeginInvoke(new SetRichTextTextDelegate(SetRichText),text);
Try using the following
if (richTextBox1.InvokeRequired)
{
richTextBox1.Invoke(new Action(delegate { richTextBox1.Text += number.ToString() + "\n"; richTextBox1.ScrollToCaret(); }));
}
else
{
richTextBox1.Text += number.ToString() + "\n";
richTextBox1.ScrollToCaret();
}
richTextBox1.Text += number.ToString() + "\n"; Can be changed as follows,
rtbEvents.AppendText(Environment.NewLine + number.ToString() );
I divided my programme in 3 layers; GUI, BL, IO and tried to grap files from my server to my pc. I made it multi threaded and zorks fine, but when i tried to add a delegate to it for sending messages from my IO to my GUI, it troubels me. It said something like:
It is not allowed to perform an operation through various threads: it
was from another thread had access to the control label download
progress than the thread on which the element is created.
What i have is this:
GUI
private void buttonDownload_Click(object sender, EventArgs e)
{
download = new BL_DataTransfer(Wat.FILM, titel, this.downloadDel);
t = new Thread(new ThreadStart(download.DownLoadFiles));
t.Start();
}
private void UpdateDownloadLabel(string File)
{
labelDownloadProgress.Text = "Downloading: " + File;
}
BL
public void DownLoadFiles()
{
//bestanden zoeken op server
string map = BASEDIR + this.wat.ToString() + #"\" + this.titel + #"\";
string[] files = IO_DataTransfer.GrapFiles(map);
//pad omvormen
string[] downloadFiles = this.VeranderNaarDownLoadPad(files,this.titel);
IO_DataTransfer.DownloadFiles(#".\" + this.titel + #"\", files, downloadFiles, this.obserdelegate);
}
IO
public static void DownloadFiles(string map, string[] bestanden, string[] uploadPlaats, ObserverDelegate observerDelegete)
{
try
{
Directory.CreateDirectory(map);
for (int i = 0; i < bestanden.Count(); i++)
{
observerDelegete(bestanden[i]);
File.Copy(bestanden[i], uploadPlaats[i]);
}
}
catch (UnauthorizedAccessException uoe) { }
catch (FileNotFoundException fnfe) { }
catch (Exception e) { }
}
Delgate
public delegate void ObserverDelegate(string fileName);
Assuming that it's the update of the label that's failing you need to marshal the event onto the UI thread. To do this change your update code to be:
private void UpdateDownloadLabel(string File)
{
if (labelDownloadProgress.InvokeRequired)
{
labelDownloadProgress.Invoke(new Action(() =>
{
labelDownloadProgress.Text = "Downloading: " + File;
});
}
else
{
labelDownloadProgress.Text = "Downloading: " + File;
}
}
I've ended up creating an extension method for this that I can call - thus reducing the amount of repeated code in my applications:
public static void InvokeIfRequired(this Control control, Action action)
{
if (control.InvokeRequired)
{
control.Invoke(action);
}
else
{
action();
}
}
Which is then called like this:
private void UpdateDownloadLabel(string File)
{
this.labelDownloadProgress.InvokeIfRequired(() =>
labelDownloadProgress.Text = "Downloading: " + File);
}
if UpdateDownloadLabel function is in some control code-file, use pattern like this:
private void UpdateDownloadLabel(string File)
{
this.Invoke(new Action(()=> {
labelDownloadProgress.Text = "Downloading: " + File;
})));
}
You need to invoke assignment on UI thread in order to be able to change something on label.