Async/Await is nice way to code responsive GUI but as I suppose cheese here isn't free. I think only the way to realize Await statement is a memory stack. Each time you call Await, there is a new pointer put in memory stack. Therefore if you call to frequently Await and result delays, you get stack overflow (that is the keyword you can't search here :))
Let's suppose we check some connection status asynchronously periodically.
Create a simple windows forms project with Form1 and form code is here.
using System;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace testasync
{
public partial class Form1 : Form
{
int CallCount = 0;
int CalBackCount = 0;
Random r = new Random();
private Button butStart = new Button();
private Button butStop = new Button();
private Timer timer1 = new Timer();
private Label labCalls =new Label();
private Label labReturns = new Label();
public Form1()
{
this.ClientSize = new System.Drawing.Size(450, 200);
butStart.Location = new System.Drawing.Point(51, 38);
butStart.Size = new System.Drawing.Size(139, 47);
butStart.Text = "Start";
butStart.Click += new System.EventHandler(this.butStart_Click);
butStop.Location = new System.Drawing.Point(237, 38);
butStop.Size = new System.Drawing.Size(139, 47);
butStop.Text = "Stop";
butStop.Click += new System.EventHandler(this.butStop_Click);
labCalls.Location = new System.Drawing.Point(48, 149);
labCalls.Size = new System.Drawing.Size(100, 23);
labReturns.Location = new System.Drawing.Point(237, 149);
labReturns.Size = new System.Drawing.Size(100, 23);
Controls.Add(this.labCalls);
Controls.Add(this.labReturns);
Controls.Add(this.butStart);
Controls.Add(this.butStop);
timer1.Interval = 5;
timer1.Tick += new System.EventHandler(this.timer1_Tick);
}
private void butStart_Click(object sender, EventArgs e)
{
timer1.Enabled = true;
}
private void butStop_Click(object sender, EventArgs e)
{
timer1.Enabled = false;
}
private async Task myWork()
{
CallCount++;
await Task.Delay(2000);
if (r.Next(100)>10) //Check Connection
await Task.Delay(100000000);
CalBackCount++;
}
private async void timer1_Tick(object sender, EventArgs e)
{
labCalls.Text = CallCount.ToString();
await myWork();
labReturns.Text = CalBackCount.ToString();
}
}
}
instead of checking connection I use random check to wait long time for connection response.
If we run the project and hit start button, we'll see that memory consumption by the application rises by time. Therefore I suppose this is not the right way to use Await statement, I think better way is use threading with delegates or nice component BackgroundWorker with progress events. Or maybe there are better solutions?
Your assumptions on how async-await works aren't correct.
Each asynchronous method will be converted in a state machine that splits the method into several methods (the parts between the start, awaits and end) and orchestrates its execution.
A rogue asynchronous method will more likely exhaust the heap than the stack.
Related
So I have a basic WPF application, that OnStartup creates a DispatcherTimer and has a tick method that runs a basic SQL query to check the status of some arbitrary table.
It is a variable query in terms of how long it takes to execute.
If I am in the main application window, when the background DispatcherTimer runs (DispatcherTimer code being within the App.xaml, not the MainWindow.xaml), the window hangs while the query runs, and as the query runs every 5 seconds, the GUI is unresponsive.
It seems a new thread is the way to go, but I am lost on where to begin. I am relatively new to C#/WPF so it's all a learning curve at the moment. How would I go about invoking my dispatcherTimer_Tickwithin a new thread, to avoid this problem?
DispatcherTimer dispatcherTimer;
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow = new MainWindow();
MainWindow.Closing += MainWindow_Closing;
_notifyIcon = new System.Windows.Forms.NotifyIcon();
_notifyIcon.DoubleClick += (s, args) => ShowMainWindow();
_notifyIcon.Icon = BackgroundApplication.Properties.Resources.GTL;
_notifyIcon.Visible = true;
CreateContextMenu();
dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Interval = new TimeSpan(0, 0, 5);
dispatcherTimer.Start();
}
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
// Console.WriteLine("Tick");
SqlConnection cnn;
connectionString = #"SOME_DATA_SOURCE";
cnn = new SqlConnection(connectionString);
cnn.Open();
// MessageBox.Show("Connection Open !");
// cnn.Close();
SqlCommand command;
SqlDataReader dataReader;
String sql = "";
Int16 output = 0;
sql = "SOME SQL STATEMENT";
command = new SqlCommand(sql, cnn);
dataReader = command.ExecuteReader();
while (dataReader.Read())
{
output = Int16.Parse(dataReader[""].ToString());
}
if(output > 0)
{
_notifyIcon.Icon = BackgroundApplication.Properties.Resources.RTL;
}
_notifyIcon.Text = "Current Issues: " + output;
}
Make the Tick handler async and await the long-running call:
private async void dispatcherTimer_Tick(object sender, EventArgs e)
{
await Task.Run(() =>
{
// make query here
});
// update the UI outside the Task
_notifyIcon.Text = "Current Issues: " + output;
}
Even better would be to directly call async methods, like
private async void dispatcherTimer_Tick(object sender, EventArgs e)
{
...
dataReader = await command.ExecuteReaderAsync();
...
}
I have a traffic exchange website and I want to convert it into an windows application using C# winform with Awesomium 1.7.5.
The basic setup is ready but there is a problem with Awesomium.
After visiting a few websites slows down and the freezes entirely ( Not Responding ).
public Form1()
{
InitializeComponent();
Text = "Traffic Exchange";
WindowState = FormWindowState.Maximized;
timer1 = new System.Windows.Forms.Timer();
timer1.Tick += new EventHandler(timer1_Tick);
int user_id = Properties.Settings.Default.user_id;
string user_id_s = user_id.ToString();
toolStripLabel2.Text = user_id_s;
if (Properties.Settings.Default.user_id == 0)
{
toolStripLabel3.Visible = true;
toolStripButton3.Visible = false;
}
else
{
toolStripButton3.Visible = true;
toolStripLabel3.Visible = false;
}
}
private void toolStripButton3_Click_1(object sender, EventArgs e)
{
// starting the traffic traffic exchange
LoadUrl();
StartTimer();
}
private void LoadUrl()
{
try
{
string MyConnection2 = "*******";
string Query = "select * from ****** where status = 1 AND credits > 5 ORDER BY rand() LIMIT 1";
MySqlConnection MyConn2 = new MySqlConnection(MyConnection2);
MySqlCommand MyCommand2 = new MySqlCommand(Query, MyConn2);
MyConn2.Open();
using (MySqlDataReader DR = MyCommand2.ExecuteReader())
{
while (DR.Read())
{
string WebURL = Convert.ToString(DR.GetValue(*));
string WebSurfSec = Convert.ToString(DR.GetValue(*));
int result = Convert.ToInt32(WebSurfSec);
int sec_to_mil = result * 1000;
toolStripLabel5.Text = WebSurfSec;
//toolStripStatusLabel2.Text = result.ToString();
//toolStripStatusLabel3.Text = sec_to_mil.ToString();
webControl3.Source = new Uri(WebURL);
toolStripTextBox1.Text = WebURL;
toolStripLabel6.Text = toolStripTextBox1.Text;
timer1.Interval = sec_to_mil; // in miliseconds
}
}
MyConn2.Close();
// WebCore.ReleaseMemory();
// webControl3.Update();
// Thread.Sleep(500);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void timer1_Tick(object sender, EventArgs e)
{
LoadUrl();
}
private void StartTimer()
{
timer1.Start();
}
So LoadUrl() it's a loop. When the app is started loads in the traffic exchange website, a little bit slow but it works and you can go form a page to another without freezing but when the exchange is in action ( LoadUrl() ) after 5 minutes the app is dead.
I've been searching for a solution all day and ended up with nothing, couldn't find a solution the problem.
The timer should not be recreated each time you loop. What is happening is that you are creating multiple event handlers each time you loop. Creating the handler once in the constructor and starting the timer in the button click routine is the right way.
You can change the interval inside the loop, but avoid adding another Start() method call there.
I am totally lost. I just seem to get no response from it.
BackgroundWorker NewWorker;
public void StartBackgroundWorker()
{
BackgroundWorker NewWorker = new BackgroundWorker();
NewWorker.DoWork += new DoWorkEventHandler(NewWorker_DoWork);
NewWorker.ProgressChanged += new ProgressChangedEventHandler(NewWorker_ProgressChanged);
NewWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(NewWorker_RunWorkerCompleted);
NewWorker.WorkerReportsProgress = true;
StartWorker();
}
void NewWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 1; (i <= 10); i++)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
ManagementObjectSearcher searcher = new ManagementObjectSearcher("Select StatusCode from Win32_PingStatus where address = 'Metabox-PC'");
ManagementObjectCollection objCollection = searcher.Get();
foreach (ManagementObject Results in objCollection)
{
MessageBox.Show(Results.ToString());
}
// Perform a time consuming operation and report progress.
System.Threading.Thread.Sleep(1);
worker.ReportProgress((i * 10));
}
}
}
private void NewWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Finished");
}
None of the message boxes appear, indicating it's finished.
What have I missed?
You're never calling RunWorkerAsync on the BackgroundWorker that you have configured.
You think you are, because you're calling StartWorker which, (I presume) contains the code NewWorker.RunWorkerAsync(); somewhere in it, however NewWorker, inside of StartWorker, is referring to an entirely different BackgroundWorker that you have not configured to do anything.
You have an instance field, NewWorker, and a local variable NewWorker inside of StartBackgroundWorker that is shadowing the instance field.
If you really need the background worker to be an instance field, then don't shadow it in StartBackgroundWorker and use the instance field throughout. If you don't need it to be an instance field (which, for the record, is likely, and needlessly promoting variables to instance fields makes for messier programs) then you simply need to start the BackgroundWorkder you created in StartBackgroundWorker. If StartWorker really has enough code that it needs to be in another method, then it probably means it should accept a BackgroundWorker as a parameter, so you can pass in the worker you created in StartBackgroundWorker.
The problem is that you never start your NewWorker. That's because your "global" NewWorker is always null.
See the fixed code:
NewWorker = new BackgroundWorker(); // this line is now fixed.
NewWorker.DoWork += new DoWorkEventHandler(NewWorker_DoWork);
NewWorker.ProgressChanged += new ProgressChangedEventHandler(NewWorker_ProgressChanged);
NewWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(NewWorker_RunWorkerCompleted);
NewWorker.WorkerReportsProgress = true;
StartWorker();
Why do you need to make the background worker a field? Just make it in the constructor and everything else like. In its events you can just cast the object as the BackgroundWorker.
public void StartBackgroundWorker()
{
BackgroundWorker NewWorker = new BackgroundWorker();
NewWorker.DoWork += new DoWorkEventHandler(NewWorker_DoWork);
NewWorker.ProgressChanged += new ProgressChangedEventHandler(NewWorker_ProgressChanged);
NewWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(NewWorker_RunWorkerCompleted);
NewWorker.WorkerReportsProgress = true;
NewWorker.RunWorkerAsync();
}
Francis is right... Any attempt to call MessageBox in a thread other than the GUI thread will block. You'll have to invoke:
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => {
MessageBox.Show("Hello World!");
}));
Thank you to all your answers. The description supplied makes more sense to me now.
Also to the previous debate: the message box does block the thread. It got stuck at 10% and then crashed my computer (VM box).
Now my only issue is that e.results is returning an array of items. But it is in object.
I am finding it difficult to get the data back.
public void StartBackgroundWorker()
{
BackgroundWorker NewWorker = new BackgroundWorker();
NewWorker.DoWork += new DoWorkEventHandler(NewWorker_DoWork);
NewWorker.ProgressChanged += new ProgressChangedEventHandler(NewWorker_ProgressChanged);
NewWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(NewWorker_RunWorkerCompleted);
NewWorker.WorkerReportsProgress = true;
NewWorker.RunWorkerAsync();
}
void NewWorker_DoWork(object sender, DoWorkEventArgs e)
{
List<string> ReturnResults = new List<string>();
BackgroundWorker worker = sender as BackgroundWorker;
ManagementObjectSearcher searcher = new ManagementObjectSearcher("Select StatusCode from Win32_PingStatus where address = 'Metabox-PC'");
ManagementObjectCollection objCollection = searcher.Get();
foreach (ManagementObject Results in objCollection)
{
ReturnResults.Add(Results["StatusCode"].ToString());
}
e.Result = ReturnResults;
// Perform a time-consuming operation and report progress.
System.Threading.Thread.Sleep(1);
}
private void NewWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
txtPingable.text = e.Result;
}
I am generating new windows via the code below and displaying a webpage there and then close it. But after a while software gives out of memory exception. So there is memory leak happening. What might be the cause and how to solve it ? Thank you.
This is how do i start new window . i am doing a loop so there are being started thousands of new windows. As you can see after 60 seconds new window get itself close.
NewWindowThread<TitleWindow, string>(c => new TitleWindow(c), "the url that is going to be displayed at new window");
private void NewWindowThread<T, P>(Func<P, T> constructor, P param) where T : Window
{
Thread thread = new Thread(() =>
{
T w = constructor(param);
w.Show();
w.Closed += (sender, e) => w.Dispatcher.InvokeShutdown();
System.Windows.Threading.Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
public class TitleWindow : Window
{
WebBrowser webnew = new WebBrowser();
public TitleWindow(string srUrl)
{
DockPanel dk = new DockPanel();
dk.Width = 900;
dk.Height = 600;
this.AddChild(dk);
webnew.Navigated += new NavigatedEventHandler(wbMain_Navigated);
System.Windows.Threading.DispatcherTimer dispatcherTimer3 = new System.Windows.Threading.DispatcherTimer();
dispatcherTimer3.Tick += new EventHandler(dispatcherTimer_Tick3);
dispatcherTimer3.Interval = new TimeSpan(0, 0, 0, 60, 0);
dispatcherTimer3.Start();
webnew.Height = 600;
webnew.Width = 900;
dk.Children.Add(webnew);
webnew.Navigate(srUrl);
this.WindowState = WindowState.Minimized;
}
void dispatcherTimer_Tick3(object sender, EventArgs e)
{
this.Close();
}
}
I immediately suspect that the WebBrowser control is not releasing it's resources when the window is closed as you are not calling it's Dispose() method. The WebBrowser control is a thin wrapper around the ActiveX MSHTML control.
Add a callback on the window Closing or Closed events to do this. e.g. webnew.Dispose().
I've googled this problem for the past week, it's killing my peace! Please help... EventArrivedEventHandler is stuck in a loop, and if I stop it, then it won't catch events. But when I use a handler method, the thread is still concentrating on the loop, and won't give attention to the new form I'm trying to make in the handler! Strange thing is, if I just use something small, like a MessageBox, it doesn't cause an issue, just trying to instantiate a form causes the buttons to NOT draw. Then shortly after the program stops responding. In case you're wondering where the form code is, it's just a standard form made by .NET, that works everywhere else in the code except for in the event handler.
Thanks!
class MainClass
{
public static void Main()
{
TaskIcon taskbarIcon;
EventWatch myWatcher;
taskbarIcon = new TaskIcon();
taskbarIcon.Show();
myWatcher = new EventWatch();
myWatcher.Start();
Application.Run();
}
}
public class TaskIcon
{
public void Show()
{
NotifyIcon notifyIcon1 = new NotifyIcon();
ContextMenu contextMenu1 = new ContextMenu();
MenuItem menuItem1 = new MenuItem();
MenuItem menuItem2 = new MenuItem();
contextMenu1.MenuItems.AddRange(new MenuItem[] { menuItem1, menuItem2 });
menuItem1.Index = 0;
menuItem1.Text = "Settings";
menuItem1.Click += new EventHandler(notifyIconClickSettings);
menuItem2.Index = 1;
menuItem2.Text = "Exit";
menuItem2.Click += new EventHandler(notifyIconClickExit);
notifyIcon1.Icon = new Icon("app.ico");
notifyIcon1.Text = "Print Andy";
notifyIcon1.ContextMenu = contextMenu1;
notifyIcon1.Visible = true;
}
private static void notifyIconClickSettings(object Sender, EventArgs e)
{
MessageBox.Show("Settings Here");
}
private static void notifyIconClickExit(object Sender, EventArgs e)
{
//taskbarIcon.Visible = false; // BONUS QUESTION: Why can't I hide the tray icon before exiting?
Application.Exit();
}
}
public class EventWatch
{
public void Start()
{
string thisUser = WindowsIdentity.GetCurrent().Name.Split('\\')[1];
WqlEventQuery query = new WqlEventQuery();
query.EventClassName = "__InstanceCreationEvent";
query.Condition = #"TargetInstance ISA 'Win32_PrintJob'";
query.WithinInterval = new TimeSpan(0, 0, 0, 0, 1);
ManagementScope scope = new ManagementScope("root\\CIMV2");
scope.Options.EnablePrivileges = true;
ManagementEventWatcher watcher = new ManagementEventWatcher(scope, query);
watcher.EventArrived += new EventArrivedEventHandler(showPrintingForm);
watcher.Start();
}
void showPrintingForm(object sender, EventArrivedEventArgs e)
{
// MessageBox.Show("This will draw just fine");
Form1 myForm;
myForm = new Form1();
myForm.Show(); // This causes a hangup
}
}
My guess would be that the ManagementEventWatcher calls the EventArrived handler from a different thread than the UI thread. Then your showPrintingForm is executed on that thread and accessing UI from a different thread than the UI thread is bad. You need to marshal your code back onto the UI thread.