Starting a GameWindow in a thread - c#

I'm trying to write an OpenTK application where you can type commands in a console window, and have a GameWindow display the result of commands.
My code looks like this:
public Program : GameWindow
{
static void Main(string[] args)
{
var display = new Program();
var task = new Task(() => display.Run());
task.Start();
while (true)
{
Console.Write("> ");
var response = Console.ReadLine();
//communicate with the display object asynchronously
}
}
}
However, the display window does not appear when started in a task or thread.
Why is this the case? I need the Run method to happen in a thread, because it is blocking for the life of the window.

To fix your particular problem, just create instance of your window ("display" in your case) on thread itself:
public class Program : GameWindow {
private static void Main(string[] args) {
Program display = null;
var task = new Thread(() => {
display = new Program();
display.Run();
});
task.Start();
while (display == null)
Thread.Yield(); // wait a bit for another thread to init variable if necessary
while (true)
{
Console.Write("> ");
var response = Console.ReadLine();
//communicate with the display object asynchronously
display.Title = response;
}
}
}

Related

Why does creating a winform object in a threadpool thread break async methods? [duplicate]

I'm trying to asynchronously show a progress form that says the application is running while the actual application is running.
As following this question, I have the following:
Main Form:
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
async Task<int> LoadDataAsync()
{
await Task.Delay(2000);
return 42;
}
private async void Run_Click(object sender, EventArgs e)
{
var runningForm = new RunningForm();
runningForm.ShowRunning();
var progressFormTask = runningForm.ShowDialogAsync();
var data = await LoadDataAsync();
runningForm.Close();
await progressFormTask;
MessageBox.Show(data.ToString());
}
}
Progress Form
public partial class RunningForm : Form
{
private readonly SynchronizationContext synchronizationContext;
public RunningForm()
{
InitializeComponent();
synchronizationContext = SynchronizationContext.Current;
}
public async void ShowRunning()
{
this.RunningLabel.Text = "Running";
int dots = 0;
await Task.Run(() =>
{
while (true)
{
UpadateUi($"Running{new string('.', dots)}");
Thread.Sleep(300);
dots = (dots == 3) ? 0 : dots + 1;
}
});
}
public void UpadateUi(string text)
{
synchronizationContext.Post(
new SendOrPostCallback(o =>
{
this.RunningLabel.Text = text;
}),
text);
}
public void CloseThread()
{
synchronizationContext.Post(
new SendOrPostCallback(o =>
{
this.Close();
}),
null);
}
}
internal static class DialogExt
{
public static async Task<DialogResult> ShowDialogAsync(this Form form)
{
await Task.Yield();
if (form.IsDisposed)
{
return DialogResult.OK;
}
return form.ShowDialog();
}
}
The above works fine, but it doesn't work when I'm calling from outside of another from. This is my console app:
class Program
{
static void Main(string[] args)
{
new Test().Run();
Console.ReadLine();
}
}
class Test
{
private RunningForm runningForm;
public async void Run()
{
var runningForm = new RunningForm();
runningForm.ShowRunning();
var progressFormTask = runningForm.ShowDialogAsync();
var data = await LoadDataAsync();
runningForm.CloseThread();
await progressFormTask;
MessageBox.Show(data.ToString());
}
async Task<int> LoadDataAsync()
{
await Task.Delay(2000);
return 42;
}
}
Watching what happens with the debugger, the process gets to await Task.Yield() and never progresses to return form.ShowDialog() and thus you never see the RunningForm. The process then goes to LoadDataAsync() and hangs forever on await Task.Delay(2000).
Why is this happening? Does it have something to do with how Tasks are prioritized (ie: Task.Yield())?
Watching what happens with the debugger, the process gets to await
Task.Yield() and never progresses to return form.ShowDialog() and thus
you never see the RunningForm. The process then goes to
LoadDataAsync() and hangs forever on await Task.Delay(2000).
Why is this happening?
What happens here is that when you do var runningForm = new RunningForm() on a console thread without any synchronization context (System.Threading.SynchronizationContext.Current is null), it implicitly creates an instance of WindowsFormsSynchronizationContext and installs it on the current thread, more on this here.
Then, when you hit await Task.Yield(), the ShowDialogAsync method returns to the caller and the await continuation is posted to that new synchronization context. However, the continuation never gets a chance to be invoked, because the current thread doesn't run a message loop and the posted messages don't get pumped. There isn't a deadlock, but the code after await Task.Yield() is never executed, so the dialog doesn't even get shown. The same is true about await Task.Delay(2000).
I'm more interested in learning why it works for WinForms and not for
Console Applications.
You need a UI thread with a message loop in your console app. Try refactoring your console app like this:
public void Run()
{
var runningForm = new RunningForm();
runningForm.Loaded += async delegate
{
runningForm.ShowRunning();
var progressFormTask = runningForm.ShowDialogAsync();
var data = await LoadDataAsync();
runningForm.Close();
await progressFormTask;
MessageBox.Show(data.ToString());
};
System.Windows.Forms.Application.Run(runningForm);
}
Here, the job of Application.Run is to start a modal message loop (and install WindowsFormsSynchronizationContext on the current thread) then show the form. The runningForm.Loaded async event handler is invoked on that synchronization context, so the logic inside it should work just as expected.
That however makes Test.Run a synchronous method, i. e., it only returns when the form is closed and the message loop has ended. If this is not what you want, you'd have to create a separate thread to run your message loop, something like I do with MessageLoopApartment here.
That said, in a typical WinForms or WPF application you should almost never need a secondary UI thread.

Difference between using Task.Yield() in a User Interface and Console App

I'm trying to asynchronously show a progress form that says the application is running while the actual application is running.
As following this question, I have the following:
Main Form:
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
async Task<int> LoadDataAsync()
{
await Task.Delay(2000);
return 42;
}
private async void Run_Click(object sender, EventArgs e)
{
var runningForm = new RunningForm();
runningForm.ShowRunning();
var progressFormTask = runningForm.ShowDialogAsync();
var data = await LoadDataAsync();
runningForm.Close();
await progressFormTask;
MessageBox.Show(data.ToString());
}
}
Progress Form
public partial class RunningForm : Form
{
private readonly SynchronizationContext synchronizationContext;
public RunningForm()
{
InitializeComponent();
synchronizationContext = SynchronizationContext.Current;
}
public async void ShowRunning()
{
this.RunningLabel.Text = "Running";
int dots = 0;
await Task.Run(() =>
{
while (true)
{
UpadateUi($"Running{new string('.', dots)}");
Thread.Sleep(300);
dots = (dots == 3) ? 0 : dots + 1;
}
});
}
public void UpadateUi(string text)
{
synchronizationContext.Post(
new SendOrPostCallback(o =>
{
this.RunningLabel.Text = text;
}),
text);
}
public void CloseThread()
{
synchronizationContext.Post(
new SendOrPostCallback(o =>
{
this.Close();
}),
null);
}
}
internal static class DialogExt
{
public static async Task<DialogResult> ShowDialogAsync(this Form form)
{
await Task.Yield();
if (form.IsDisposed)
{
return DialogResult.OK;
}
return form.ShowDialog();
}
}
The above works fine, but it doesn't work when I'm calling from outside of another from. This is my console app:
class Program
{
static void Main(string[] args)
{
new Test().Run();
Console.ReadLine();
}
}
class Test
{
private RunningForm runningForm;
public async void Run()
{
var runningForm = new RunningForm();
runningForm.ShowRunning();
var progressFormTask = runningForm.ShowDialogAsync();
var data = await LoadDataAsync();
runningForm.CloseThread();
await progressFormTask;
MessageBox.Show(data.ToString());
}
async Task<int> LoadDataAsync()
{
await Task.Delay(2000);
return 42;
}
}
Watching what happens with the debugger, the process gets to await Task.Yield() and never progresses to return form.ShowDialog() and thus you never see the RunningForm. The process then goes to LoadDataAsync() and hangs forever on await Task.Delay(2000).
Why is this happening? Does it have something to do with how Tasks are prioritized (ie: Task.Yield())?
Watching what happens with the debugger, the process gets to await
Task.Yield() and never progresses to return form.ShowDialog() and thus
you never see the RunningForm. The process then goes to
LoadDataAsync() and hangs forever on await Task.Delay(2000).
Why is this happening?
What happens here is that when you do var runningForm = new RunningForm() on a console thread without any synchronization context (System.Threading.SynchronizationContext.Current is null), it implicitly creates an instance of WindowsFormsSynchronizationContext and installs it on the current thread, more on this here.
Then, when you hit await Task.Yield(), the ShowDialogAsync method returns to the caller and the await continuation is posted to that new synchronization context. However, the continuation never gets a chance to be invoked, because the current thread doesn't run a message loop and the posted messages don't get pumped. There isn't a deadlock, but the code after await Task.Yield() is never executed, so the dialog doesn't even get shown. The same is true about await Task.Delay(2000).
I'm more interested in learning why it works for WinForms and not for
Console Applications.
You need a UI thread with a message loop in your console app. Try refactoring your console app like this:
public void Run()
{
var runningForm = new RunningForm();
runningForm.Loaded += async delegate
{
runningForm.ShowRunning();
var progressFormTask = runningForm.ShowDialogAsync();
var data = await LoadDataAsync();
runningForm.Close();
await progressFormTask;
MessageBox.Show(data.ToString());
};
System.Windows.Forms.Application.Run(runningForm);
}
Here, the job of Application.Run is to start a modal message loop (and install WindowsFormsSynchronizationContext on the current thread) then show the form. The runningForm.Loaded async event handler is invoked on that synchronization context, so the logic inside it should work just as expected.
That however makes Test.Run a synchronous method, i. e., it only returns when the form is closed and the message loop has ended. If this is not what you want, you'd have to create a separate thread to run your message loop, something like I do with MessageLoopApartment here.
That said, in a typical WinForms or WPF application you should almost never need a secondary UI thread.

How i can get Process.waitForFinish()

I'm trying to get the pid of a process when the process end working but not yet killing.
process.WaitForExit();
the method above stop until the process is killed, but i'm looking for something like process.waitForFinish because i'm trying to handle the process when it finishing working but not yet killed.
Like adding a signal and get in the other process. exemple:
//Process to catch
main(){
//Do some Code
End of the function
SendEndSignal(); & Stop();
}
Second process will catch first process like that
main(){
int pid = Process.start("Process1.exe");
process.waitUntilReceiveSignal();
//Do code here
process.kill();
}
I need something like process.WaitForInputIdle(); but the problem here is it working only if the process contain graphical interface. So what about if the process wait for input from user by console like using Console.readLine().
This mean that i need to know when the process go to inactive state but not killed.
EventWaitHandle is a way to go in your case.
namespace A
{
class Program
{
static void Main(string[] args)
{
var evh = new EventWaitHandle(false, EventResetMode.AutoReset,"fromA");
Console.WriteLine("Anykey to send signal");
Console.ReadKey();
evh.Set();
}
}
}
namespace B
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Waiting for a signal");
var evh = EventWaitHandle.OpenExisting("fromA");
evh.WaitOne();
Console.WriteLine("Signal!");
Console.ReadKey();
}
}
}
But keep in mind you will need to wait for child process to register its EventWaitHandle. So you either do wait for some time (ugly). Or create another EventWaitHandle in parent process, so child process notifies when its handler is registered
Edit: Complete solution
namespace Parent
{
class Program
{
static void Main(string[] args)
{
using (var childReadyEh =new EventWaitHandle(false, EventResetMode.AutoReset,"childIsReady"))
{
Process.Start("child.exe");
childReadyEh.WaitOne();
}
using (var childSignalEh =EventWaitHandle.OpenExisting("childSignal"))
{
childSignalEh.WaitOne();
}
Console.WriteLine("Signal is recieved");
Console.ReadKey();
}
}
}
namespace Child
{
class Program
{
static void Main(string[] args)
{
EventWaitHandle childSignalEh =null;
using (var childReadyEh =EventWaitHandle.OpenExisting("childIsReady"))
{
childSignalEh =new EventWaitHandle(false, EventResetMode.AutoReset,"childSignal");
childReadyEh.Set();
}
Console.WriteLine("Anykey to send a signal to parent");
Console.ReadKey();
childSignalEh.Set();
childSignalEh.Dispose();
Console.WriteLine("Signal is sent");
Console.ReadKey();
}
}
}

TPL Equivalent of Thread Class 'Splash-Type' Screen

Given the class below, to launch a splash screen on an alternate thread:
public partial class SplashForm : Form
{
private static Thread _splashThread;
private static SplashForm _splashForm;
public SplashForm()
{
InitializeComponent();
}
// Show the Splash Screen (Loading...)
public static void ShowSplash()
{
if (_splashThread == null)
{
// Show the form in a new thread.
_splashThread = new Thread(new ThreadStart(DoShowSplash));
_splashThread.IsBackground = true;
_splashThread.Start();
}
}
// Called by the thread.
private static void DoShowSplash()
{
if (_splashForm == null)
_splashForm = new SplashForm();
// Create a new message pump on this thread (started from ShowSplash).
Application.Run(_splashForm);
}
// Close the splash (Loading...) screen.
public static void CloseSplash()
{
// Need to call on the thread that launched this splash.
if (_splashForm.InvokeRequired)
_splashForm.Invoke(new MethodInvoker(CloseSplash));
else
Application.ExitThread();
}
}
This is called and closed with the following respective commands
SplashForm.ShowSplash();
SplashForm.CloseSplash();
Fine.
I am not exactly new to the TPL, of course we can show the form on another thread using something as simple as:
Task task = Task.Factory.StartNew(() =>
{
SomeForm someForm = new SomeForm();
someForm.ShowDialog();
};
My issue is closing this SomeForm down when you are ready. There must be a better way than creating a public static method in the SomeForm class like
private static SomeForm _someForm;
public static void CloseSomeForm()
{
if (_someForm.InvokeRequired)
_someForm.Invoke(new MethodInvoker(CloseSomeForm));
}
My question is, what is the best way to do the same thing as shown using the SplashForm class above using the Task Parrallel Library (TPL)? Specifically, the best way to close the form invoked on another thread from the UI.
Your question does not seem to be so much about a difference between Thread and Task because what you want is to get rid of the "dirty" static state. I suggest you encapsulate it into a class:
class SplashController
{
public void Run() {
_someForm = new SomeForm();
someForm.ShowDialog();
}
private SomeForm _someForm;
public void CloseSomeForm()
{
if (_someForm.InvokeRequired)
_someForm.Invoke(new MethodInvoker(CloseSomeForm));
}
}
You can call Run using whatever threading mechanism you like. CloseSomeForm does not use threading so it is independent of this problem.
You can now store a reference to an instance of SplashController wherever you like. In local variables or indeed in a static variable. The latter makes sense because there is exactly one splash screen.
Because the static state is now well encapsulated I don't see any problem with it being statically held.
You probably shouldn't do something like this
Task task = Task.Factory.StartNew(() =>
{
SomeForm someForm = new SomeForm();
someForm.ShowDialog();
};
because it would require a message loop to be present on the exact thread that creates the Form, which is a ThreadPool thread. But I haven't tested this.
You could try this:
public static Task<SplashForm> ShowSplash()
{
var tcs = new TaskCompletionSource<SplashForm>();
// Show the form in a new thread.
_splashThread = new Thread(() =>
{
var splashForm = new SplashForm();
tcs.SetResult(_splashForm);
// Create a new message pump on this thread (started from ShowSplash).
Application.Run(splashForm);
});
_splashThread.IsBackground = true;
_splashThread.Start();
}
this would allow you to remove the static modifier from CloseSplash:
// Close the splash (Loading...) screen.
public void CloseSplash()
{
// Need to call on the thread that launched this splash.
if (this.InvokeRequired)
this.Invoke(new MethodInvoker(CloseSplash));
else
Application.ExitThread();
}
May be used like this:
var form = await SplashForm.ShowSplash();
form.CloseSplash();

Non-terminating / non-blocking Windows Mobile App

I have a console app that I want to run continually in the background. I thought that if I started it up and then told it to wait things would work. But when I have it wait, it freezes the application.
Here is my code:
class Program
{
static public ManualResetEvent StopMain;
static void Main(string[] args)
{
// Hide the cursor.
Cursor.Current = Cursors.Default;
StopMain = new ManualResetEvent(false);
RunHook runHook = new RunHook();
// wait until signalled by Program.StopMain.Set();
StopMain.WaitOne();
}
}
class RunHook
{
private HookKeys hook;
public RunHook()
{
hook = new HookKeys();
hook.HookEvent += EventForHook;
}
private void EventForHook(HookEventArgs e, KeyBoardInfo keyBoardInfo,
ref Boolean handled)
{
if ((keyBoardInfo.scanCode == 4) && (keyBoardInfo.vkCode == 114))
handled = true;
}
}
Any ideas on how to have this run in the background but never terminate?
The behavior you see is expected. You have one thread, and it's in a wait state. To get some form of activity, you have to let the scheduler actually do something. A background thread is one way to achieve this:
static void Main(string[] args)
{
StopMain = new ManualResetEvent(false);
bool exit = false;
new Thread(
delegate
{
new RunHook();
while(!exit) { Thread.Sleep(1); }
}
).Start();
StopMain.WaitOne();
exit = true;
}
Another is to just let the primary thread yield:
static void Main(string[] args)
{
StopMain = new ManualResetEvent(false);
RunHook runHook = new RunHook();
while(!StopMain.WaitOne())
{
Thread.Sleep(1);
}
}
There are certainly other ways, too. Personally I'd do neither of these. Instead I'd add a blocking method to the RunHook class and have it return when it was done or signalled.

Categories