0

The problem is that dispatcher blocks the MainWindow thread until it is executed all of the queued AddOutputLine()s. Can these calls be non-blocking for a wpf control?

    //TaskPage.xaml.cs
    [MTAThread]
    public void AddOutputLine(string line = "")
    {
        try {
            Session.TaskPage.Dispatcher.BeginInvoke(new Action(delegate
            {
                txtOutput.Text += "[" + DateTime.Now + "] " + line + Environment.NewLine;
                txtOutput.ScrollToEnd();
            }));
        } catch (Exception ex) {
            Program.SetCurrentStatus("Error occured : "+ex.Message);
        }
    }

In this class that invokes the Action parameter:

//BackgroundUploader.cs
private Action<string> _log;
public BackgroundUploader(Client client, Action<string> log = null)
{
    _log = log;
    _client = client;
}

while doing this:

    //BackgroundUploader.cs
    public void RunThreadProc()
    {
        try
        {
            _log?.Invoke("Timeout interval set for BackGround Task.");
            while (!Stop)
            {
                //the problematic blocking of WPF Control starts here
                var inOutFiles = CreateXML(_sqlStrings, _outputNames, _outputDirectory);
                Send(inOutFiles, _outputDirectory);
                LastRunTime = DateTime.Now;
                _log?.Invoke($"Waiting for {_waitMinutes} minutes...");
                //the Control returns to processing messages
                System.Threading.Thread.Sleep((int)TimeSpan.FromMinutes(_waitMinutes).TotalMilliseconds);
            }
            _log?.Invoke("BackGroundTask canceled.");
        }
        catch (Exception ex)
        {
            _log?.Invoke($"Error while executing background task: {ex.Message}");
        }
    }

The RunThreadProc() is called from Control like this:

//TaskPage.xaml.cs
_backgroundThread = new System.Threading.Thread(bU.RunThreadProc);
_backGroundThread.Start();

I also cannot use Bcl.Async because it's failing on Task result with method that uses async modifier on Windows Server 2003 target OS.

edit: I encountered the problem inside of the background class, its methods run synchronously, even though they are in a different thread.

Ivan Silkin
  • 381
  • 1
  • 7
  • 15
  • 1
    when you flood the app with messages that will modify a control content which needs a repainting, then yes that will block. You should update the UI only about 25 times per second - as normal human beings will not see any difference ;o) – Sir Rufo Jan 28 '22 at 09:56
  • Have a look at https://stackoverflow.com/a/23443359/7740926 – rufw91 Jan 28 '22 at 11:02
  • The `Microsoft.Bcl.Async` package was created to allow using `async/await` for precisely such systems (Windows XP, Windows 2003). What was the *actual* problem when you tried using it? – Panagiotis Kanavos Jan 28 '22 at 11:28
  • Apart from that, Windows 2003 is *way* too old and insecure. It reached End of Life in 2015. It's way past time this was replaced by an up-to-date system. This isn't a "yes in an ideal world" situation, this is a "liable for any security breaches and problems" situation. This machine can't connect to any service any more, as all services require TLS algorithms that were never available in Windows 2003 – Panagiotis Kanavos Jan 28 '22 at 11:31
  • 1
    There are things you can do to improve performance though. A TextBox simply isn't a log file. Modifying the `TextBox.Text` property over and over is simply bad and hugely inefficient coding. No amount of threading can fix this. Use data binding instead to bind the control to a property, and only raise the NotifyChanged event when you really want to update the screen. Even better, use a `List` property and bind a ListView to it. Instead of creating new strings over end over, you'll only need to append items to that List. WPF will only render the visible items too – Panagiotis Kanavos Jan 28 '22 at 11:37
  • @PanagiotisKanavos, this server is a local virtual machine that's used for a narrow range of tasks, I didn't choose it myself. The error with Microsoft.Bcl.Async was "System.AggreagteException, at System.Threading.Tasks.TaskExceptionHolder.Finilize()" (Source: .NET Runtime, ID 1026, Framework Version: 4.0.30319) – Ivan Silkin Jan 28 '22 at 11:37
  • That didn't come from Bcl.Async, it means one of your tasks threw an exception that was never handled. It's a bug in your application, not Tasks. Had you used a `try` block in your code the exception would be caught. If you used `ContinueWith`, checking for a faulted task would also work. Use tasks instead of manually trying to use threads. They work just fine. Use `Bcl.Async` and eg `await Task.Run` instead of manually starting threads. That works as well – Panagiotis Kanavos Jan 28 '22 at 11:40
  • @PanagiotisKanavos, I didn't experience this type of exception when I run the application under Windows 10, and even Windows 7. – Ivan Silkin Jan 28 '22 at 11:41
  • Because you don't use just tasks there, you use `await`, which raises any exceptions. In your Windows 2003 code though, you started a task, never called `.Wait()` or `.Result` on it so its exceptions were never noticed, until the GC tried to garbage collect the dead task – Panagiotis Kanavos Jan 28 '22 at 11:42
  • The docs explain how to handle unhandled *task* exceptions, the same way you can handle unhandled application exceptions, to avoid crashing the application – Panagiotis Kanavos Jan 28 '22 at 11:44
  • @PanagiotisKanavos, the problem with "await" is also the fact that for this particular solution to be successfully compiled and run under Win32 x86 machienes without Win32s API, I use Visual Studio 2010 to create the final assembly, and it has no "await" operator there ... – Ivan Silkin Jan 28 '22 at 11:46
  • .NET Framework 4.5, back in 2012, changed the default behavior and unobserved task exception no longer terminate the application. You'll find a lot of duplicate questions for your real problem, like [this one](https://stackoverflow.com/questions/2707295/how-to-handle-all-unhandled-exceptions-when-using-task-parallel-library) Handle the [TaskScheduler.UnobservedTaskException](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskscheduler.unobservedtaskexception?redirectedfrom=MSDN&view=netframework-4.0) event – Panagiotis Kanavos Jan 28 '22 at 11:47
  • 1
    @IvanSilkin don't use VS 2010 then, use VS 2012. This can still target Windows 2003. The real problem is a bug in your code though, not Tasks. Your code throws and never handles an exception. This would crash your application *the same way* any other unhandled exception would – Panagiotis Kanavos Jan 28 '22 at 11:48
  • @PanagiotisKanavos, thanks for the tip of binding to ListView, I only changed the type of the collection from List to ObservableColletion for the ListView to be notified every time a new row is added. And it solves the problem (at least when starting new threads, without async/await). – Ivan Silkin Jan 28 '22 at 15:00

0 Answers0