I am new to the thread model in .NET. What would you use to:
Start a process that handles a file (process.StartInfo.FileName = fileName;).
Wait for the user to close the process OR abandon the thread after some time.
If the user closed the process, delete the file.
Starting the process and waiting should be done on a different thread than the main thread, because this operation should not affect the application.
Example:
My application produces an html report. The user can right click somewhere and say "View Report" - now I retrieve the report contents in a temporary file and launch the process that handles html files i.e. the default browser. The problem is that I cannot cleanup, i.e. delete the temp file.
"and waiting must be async" - I'm not trying to be funny, but isn't that a contradiction in terms? However, since you are starting a Process, the Exited event may help:
ProcessStartInfo startInfo = null;
Process process = Process.Start(startInfo);
process.EnableRaisingEvents = true;
process.Exited += delegate {/* clean up*/};
If you want to actually wait (timeout etc), then:
if(process.WaitForExit(timeout)) {
// user exited
} else {
// timeout (perhaps process.Kill();)
}
For waiting async, perhaps just use a different thread?
ThreadPool.QueueUserWorkItem(delegate {
Process process = Process.Start(startInfo);
if(process.WaitForExit(timeout)) {
// user exited
} else {
// timeout
}
});
Adding an advanced alternative to this old question. If you want to wait for a process to exit without blocking any thread and still support timeouts, try the following:
public static Task<bool> WaitForExitAsync(this Process process, TimeSpan timeout)
{
ManualResetEvent processWaitObject = new ManualResetEvent(false);
processWaitObject.SafeWaitHandle = new SafeWaitHandle(process.Handle, false);
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
RegisteredWaitHandle registeredProcessWaitHandle = null;
registeredProcessWaitHandle = ThreadPool.RegisterWaitForSingleObject(
processWaitObject,
delegate(object state, bool timedOut)
{
if (!timedOut)
{
registeredProcessWaitHandle.Unregister(null);
}
processWaitObject.Dispose();
tcs.SetResult(!timedOut);
},
null /* state */,
timeout,
true /* executeOnlyOnce */);
return tcs.Task;
}
Again, the advantage to this approach compared to the accepted answer is that you're not blocking any threads, which reduces the overhead of your app.
Try the following code.
public void KickOffProcess(string filePath) {
var proc = Process.Start(filePath);
ThreadPool.QueueUserWorkItem(new WaitCallBack(WaitForProc), proc);
}
private void WaitForProc(object obj) {
var proc = (Process)obj;
proc.WaitForExit();
// Do the file deletion here
}
The .NET 5 introduced the new API Process.WaitForExitAsync, that allows to wait asynchronously for the completion of a process. It offers the same functionality with the existing Process.WaitForExit, with the only difference being that the waiting is asynchronous, so it does not block the calling thread.
Usage example:
private async void button1_Click(object sender, EventArgs e)
{
string filePath = Path.Combine
(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
Guid.NewGuid().ToString() + ".txt"
);
File.WriteAllText(filePath, "Hello World!");
try
{
using Process process = new();
process.StartInfo.FileName = "Notepad.exe";
process.StartInfo.Arguments = filePath;
process.Start();
await process.WaitForExitAsync();
}
finally
{
File.Delete(filePath);
}
MessageBox.Show("Done!");
}
In the above example the UI remains responsive while the user interacts with the opened file. The UI thread would be blocked if the WaitForExit had been used instead.
I would probably not use a separate process for opening a file. Instead, I'd probably utilize a background thread (if I thought the operation was going to take a long time and possible block the UI thread).
private delegate void FileOpenDelegate(string filename);
public void OpenFile(string filename)
{
FileOpenDelegate fileOpenDelegate = OpenFileAsync;
AsyncCallback callback = AsyncCompleteMethod;
fileOpenDelegate.BeginInvoke(filename, callback, state);
}
private void OpenFileAsync(string filename)
{
// file opening code here, and then do whatever with the file
}
Of course, this is not a good working example (it returns nothing) and I haven't shown how the UI gets updated (you have to use BeginInvoke at the UI level because a background thread cannot update the UI thread). But this approach is generally how I go about handling asynchronous operations in .Net.
You can use the Exited event in Process class
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "notepad.exe";
Process process = Process.Start(info);
process.Exited += new EventHandler(process_Exited);
Console.Read();
and in that event you can handle the operations you mentioned
Related
I have a situation that i export data to a file and what i have been asked to do is to provide a cancel button which on click will stop the export if it takes too much time to export.
I started exporting to the file in a thread. And i try to abort the thread on the button click. But it do not work.
I searched on Google and i found that abort() is not recommended. But what else should I choose to achieve it?
My current code is:
private void ExportButtonClick(object param)
{
IList<Ur1R2_Time_Points> data = ct.T_UR.ToList();
DataTable dtData = ExportHelper.ToDataTable(data);
thread = new Thread(new ThreadStart(()=>ExportHelper.DataTableToCsv(dtData, "ExportFile.csv")));
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Name = "PDF";
thread.Start();
}
private void StopButtonClick(object param)
{
if (thread.Name == "PDF")
{
thread.Interrupt();
thread.Abort();
}
}
Aborting a thread is a bad idea, especially when dealing with files. You won't have a chance to clean up half-written files or clean-up inconsistent state.
It won't harm the .NET Runtime bat it can hurt your own application eg if the worker method leaves global state, files or database records in an inconsistent state.
It's always preferable to use cooperative cancellation - the thread periodically checks a coordination construct like a ManualResetEvent or CancellationToken. You can't use a simple variable like a Boolean flag, as this can lead to race conditions, eg if two or more threads try to set it at the same time.
You can read about cancellation in .NET in the Cancellation in Managed Threads section of MSDN.
The CancellationToken/CancellationTokenSource classes were added in .NET 4 to make cancellation easier that passing around events.
In your case, you should modify your DataTableToCsv to accept a CancellationToken. That token is generated by a CancellationTokenSource class.
When you call CancellationTokenSource.Cancel the token's IsCancellationRequested property becomes true. Your DataTableToCsv method should check this flag periodically. If it's set, it should exit any loops, delete any inconsistent files etc.
Timeouts are directly supported with CancelAfter. Essentially, CancelAfter starts a timer that will fire Cancel when it expires.
Your code could look like this:
CancellationTokenSource _exportCts = null;
private void ExportButtonClick(object param)
{
IList<Ur1R2_Time_Points> data = ct.T_UR.ToList();
DataTable dtData = ExportHelper.ToDataTable(data);
_exportCts=new CancellationTokenSource();
var token=_exportCts.Token;
thread = new Thread(new ThreadStart(()=>
ExportHelper.DataTableToCsv(dtData, "ExportFile.csv",token)));
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Name = "PDF";
_exportCts.CancelAfter(10000);
thread.Start();
}
private void StopButtonClick(object param)
{
if (_exportCts!=null)
{
_exportCts.Cancel();
}
}
DataTableToCsv should contain code similar to this:
foreach(var row in myTable)
{
if (token.IsCancellationRequested)
{
break;
}
//else continue with processing
var line=String.Join(",", row.ItemArray);
writer.WriteLine(line);
}
You can clean up your code quite a bit by using tasks instead of raw threads:
private async void ExportButtonClick(object param)
{
IList<Ur1R2_Time_Points> data = ct.T_UR.ToList();
DataTable dtData = ExportHelper.ToDataTable(data);
_exportCts=new CancellationTokenSource();
var token=_exportCts.Token;
_exportCts.CancelAfter(10000);
await Task.Run(()=> ExportHelper.DataTableToCsv(dtData, "ExportFile.csv",token)));
MessageBox.Show("Finished");
}
You could also speed it up by using asynchronous operations, eg to read data from the database or write to text files without blocking or using threads. Windows IO (both file and network) is asynchronous at the driver level. Methods like File.WriteLineAsync don't use threads to write to a file.
Your Export button handler could become :
private void ExportButtonClick(object param)
{
IList<Ur1R2_Time_Points> data = ct.T_UR.ToList();
DataTable dtData = ExportHelper.ToDataTable(data);
_exportCts=new CancellationTokenSource();
var token=_exportCts.Token;
_exportCts.CancelAfter(10000);
await Task.Run(async ()=> ExportHelper.DataTableToCsv(dtData, "ExportFile.csv",token)));
MessageBox.Show("Finished");
}
and DataTableToCsv :
public async Task DataTableToCsv(DataTable table, string file,CancellationToken token)
{
...
foreach(var row in myTable)
{
if (token.IsCancellationRequested)
{
break;
}
//else continue with processing
var line=String.Join(",", row.ItemArray);
await writer.WriteLineAsync(line);
}
You can use a boolean flag. Use a volatile boolean for that.
In the helper do something like:
this.aborted = false;
while(!finished && !aborted) {
//process one row
}
Whenever you want to cancel the operation, you call a method to set aborted to true:
public void Abort() {
this.aborted = true;
}
Have a read here: https://msdn.microsoft.com/en-us/library/system.threading.threadabortexception(v=vs.110).aspx
When a call is made to the Abort method to destroy a thread, the common language runtime throws a ThreadAbortException. ThreadAbortException is a special exception that can be caught, but it will automatically be raised again at the end of the catch block. When this exception is raised, the runtime executes all the finally blocks before ending the thread. Because the thread can do an unbounded computation in the finally blocks or call Thread.ResetAbort to cancel the abort, there is no guarantee that the thread will ever end. If you want to wait until the aborted thread has ended, you can call the Thread.Join method. Join is a blocking call that does not return until the thread actually stops executing.
Since Thread.Abort() is executed by another thread, it can happen anytime and when it happens ThreadAbortException is thrown on target thread.
Inside ExportHelper.DataTableToCsv:
catch(ThreadAbortException e) {
Thread.ResetAbort();
}
On StopButtonClick
if (thread.Name == "PDF")
{
thread.Interrupt();
thread.Join();
}
To Stop a thread you have one option of Thread.Abort.However because this method thrown ThreadAbortException on the target thread when it executed by another thead.
Which is not recommended.
The second option to stop a thread is by using shared variable that both your target and your calling thread can access.
See the Example ::
public static class Program
{
public static void ThreadMethod(object o)
{
for (int i = 0; i < (int)o; i++)
{
Console.WriteLine("ThreadProc: { 0}", i);
Thread.Sleep(0);
}
}
public static void Main()
{
bool stopped = false;
Thread t = new Thread(new ThreadStart(() =>
{
while (!stopped)
{
Console.WriteLine("Running...");
Thread.Sleep(1000);
}
}));
t.Start();
Console.WriteLine("Press any key to exit");
Console.ReadKey();
stopped = true;
t.Join();
}
}
//Source :: Book --> Programming in c#
I want to ensure that my logic is correct here. I want to run a process for timeout seconds, if it runs for longer it should be immediately killed.
The completed flag should reliably indicate whether the process completed as intended, e.g was not killed, and did not crash or throw an exception.
Also, I am not positive if the check to process.HasExited is correct. If process.WaitForExit() returns false and Kill() succeeds, then will process.HasExited always be true? That would be my assumption but I wanted to confirm. Also, what if anything can be done if Kill() fails,
besides just logging?
using (process = new Process())
{
process.EnableRaisingEvents = true;
process.OutputDataReceived += new DataReceivedEventHandler(OnOutputDataReceived);
process.ErrorDataReceived += new DataReceivedEventHandler(OnErrorDataReceived);
process.Exited += new EventHandler(OnExited);
process.StartInfo = startInfo;
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
if (!process.WaitForExit(timeout))
{
try
{
process.Kill();
}
catch (Exception e)
{
LogError(e, MethodBase.GetCurrentMethod());
}
finally
{
this.completed = false;
}
}
else
{
if (process.HasExited)
{
this.code = process.ExitCode;
this.completed = true;
}
else
{
this.completed = false;
}
}
}
Yes, the HasExited will always be true in your case.
According to MSDN,
"A value of true for HasExited indicates that the associated process has terminated, either normally or abnormally.[...]A process can terminate independently of your code. If you started the process using this component, the system updates the value of HasExited automatically, even if the associated process exits independently."
However, if your process crashes and terminates before your timeout, then your code will set it as completed anyway. Maybe you should check the exit code, but it can have different meanings for each process:
if (process.ExitCode != 0)
{
this.completed = false;
}
For crashes, there are some approaches here and here, but generally you can't detect crashes for all processes.
We use the following in a .net console app
private void InitTimer()
{
double lInterval = Convert.ToDouble(AppSettings("MaxExecutionTime"));
lInterval = lInterval * 60 * 1000;
tm = new System.Timers.Timer(lInterval); // global timer object
tm.Elapsed += OnTimedEvent;
tm.Enabled = true;
}
public void ThreadProc(object stateinfo)
{
// set error code here
Environment.Exit(0);
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
Threading.ThreadPool.QueueUserWorkItem(new Threading.WaitCallback(ThreadProc));
}
In C, you could set an OS alarm using the alarm function. When it expired, a SIGALRM would be sent to your process, which kills it if no handler is set.
You can use this. It is a C# wrapper over the JobObjects functionallity.
The idea behind is (low level outline that is embedded inside the library I mentioned):
Create a job object.
Configure the job object to have a time limit of x seconds.
Create a process and before resuming it assing it to the job object.
Resume the process.
The process will be killed by the operating system when the time passes. YOu usually get notified by a non zero return code, or a callback. The JobObject API itself allows callbacks, not sure about the C# wrapper.
Also using job objects you can restrict memory usage.
On the page I mentioned you can find examples also.
UPDATE
After I wrote the above statements I have found this Kill child process when parent process is killed. They use the JobObjects for another task, but the usage of JobObjects should be the same as for your case.
I am trying to run several external application from inside my application. Assume that I want to run an application called LongtimeRun.exe for 10 times and each time that this applications runs, it takes around 30s to finish ( total time is 300 sec or 5 minutes!). I also want to give user some progress indication ( for example how many times the application runs).
I can create a batch file and run LongTimeRun.exe there 10 times, but then I am not able to show any progress report.
I have this code which works:
using System.Diagnostics;
using System.IO;
public class CommandProcessor
{
private readonly string binDirectory;
private readonly string workingDirectory;
public CommandProcessor(string workingDirectory, string binFolderName)
{
binDirectory = Path.Combine(FileSystem.ApplicationDirectory, binFolderName);
this.workingDirectory = workingDirectory;
}
public int RunCommand(string command, string argbase, params string[] args)
{
var commandPath = Path.Combine(binDirectory, command);
var formattedArgumets = string.Format(argbase, args);
var myProcess = new Process();
myProcess.EnableRaisingEvents = false;
myProcess.StartInfo.FileName = commandPath;
myProcess.StartInfo.Arguments = formattedArgumets;
myProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
myProcess.StartInfo.WorkingDirectory = this.workingDirectory;
myProcess.Start();
myProcess.WaitForExit();
}
}
When I calling it in tis way:
private void RunCommands()
{
var command = "LongRunCommand.exe";
string binDirectory = Path.Combine(FileSystem.ApplicationDirectory, binFolderName);
var cp = new CommandProcessor(this.workingDirectory, binDirectory);
for(int i=0;i<10;i++)
{
cp.RunCommand(Command, "-i {0}", i);
}
}
The above code is called as part of direct call and blocks the application (the applications seems to hangs during this process.
To solve the hanging problem, I used a backgroundworker as follow:
var worker = new BackgroundWorker();
worker.DoWork += this.WorkerDoWork;
worker.RunWorkerCompleted += this.workerRunWorkerCompleted;
worker.RunWorkerAsync();
and called runcommand inside WorkerDoWork.
Now the application exited after it called this line:
myProcess.WaitForExit();
There is no debug info and exit code is -1.
What is the problem and how can solve it?
Is there any better way to achieve my goal without using BackgroundWorker?
The problem you are encountering is because your BackgroundWorker threads are still running but you application completes its life-cycle and ends (it is not being blocked by them so its path is clear to end) therefore killing these threads.
You need to inform the application NOT to exit while the background threads are still running. You could have a counter that is incremented when each thread starts and then as they complete they can decrement the counter.
Inside your main application thread you could wait until the counter reaches zero before ending the application.
Obviously you will need to take into account locking (i.e. two threads try to decrement counter at the same time) but this should give you a starter.
I make a thread which do some work and run shutdown.exe to shutdown the pc.
Worker work = new Worker();
Thread thread = new Thread(new ThreadStart(work.DoWork));
thread.Start();
and the method DoWork()
public void DoWork()
{
/* Do some thing */
// This will shutdown the PC
ProcessStartInfo startInfo = new ProcessStartInfo(Environment.GetFolderPath(System.Environment.SpecialFolder.System) + #"\shutdown.exe", "-s -t 5");
Process.Start(startInfo);
}
If i call method work.DoWork() in main thread, the PC'll shutdown.
But if i put it in thread using thread.Start(), the pc won't shutdown.
Edit:
Found my mistake. I create a thread-safe call method to read the checkbox which always return false
delegate bool GetcbShutdownCheckedValueCallback();
public bool GetcbShutdownCheckedValue()
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.lblCraftRemain.InvokeRequired)
{
GetcbShutdownCheckedValueCallback d = new GetcbShutdownCheckedValueCallback(GetcbShutdownCheckedValue);
this.Invoke(d);
}
else
{
return cbShutdown.Checked;
}
return false;
}
I call the method to check if the check box is checked then shutdown. So actually the code isn't executed.
If you want to shut the computer down it's better to call Win32's ExitWindows function rather than running the shutdown.exe program.
There's MSDN documentation here: http://msdn.microsoft.com/en-us/library/windows/desktop/aa376867%28v=vs.85%29.aspx
And the C# P/Invoke signature here: http://www.pinvoke.net/default.aspx/user32.exitwindowsex
I am looking for a way to call a method on a new thread (using C#).
For instance, I would like to call SecondFoo() on a new thread. However, I would then like to have the thread terminated when SecondFoo() finishes.
I have seen several examples of threading in C#, but none that apply to this specific scenario where I need the spawned thread to terminate itself. Is this possible?
How can I force the spawned thread running Secondfoo() to terminate upon completion?
Has anyone come across any examples of this?
If you actually start a new thread, that thread will terminate when the method finishes:
Thread thread = new Thread(SecondFoo);
thread.Start();
Now SecondFoo will be called in the new thread, and the thread will terminate when it completes.
Did you actually mean that you wanted the thread to terminate when the method in the calling thread completes?
EDIT: Note that starting a thread is a reasonably expensive operation. Do you definitely need a brand new thread rather than using a threadpool thread? Consider using ThreadPool.QueueUserWorkItem or (preferrably, if you're using .NET 4) TaskFactory.StartNew.
Does it really have to be a thread, or can it be a task too?
if so, the easiest way is:
Task.Factory.StartNew(() => SecondFoo());
Once a thread is started, it is not necessary to retain a reference to the Thread object. The thread continues to execute until the thread procedure ends.
new Thread(new ThreadStart(SecondFoo)).Start();
Asynchronous version:
private async Task DoAsync()
{
await Task.Run(async () =>
{
//Do something awaitable here
});
}
Unless you have a special situation that requires a non thread-pool thread, just use a thread pool thread like this:
Action secondFooAsync = new Action(SecondFoo);
secondFooAsync.BeginInvoke(new AsyncCallback(result =>
{
(result.AsyncState as Action).EndInvoke(result);
}), secondFooAsync);
Gaurantees that EndInvoke is called to take care of the clean up for you.
As far as I understand you need mean terminate as Thread.Abort() right? In this case, you can just exit the Foo(). Or you can use Process to catch the thread.
Thread myThread = new Thread(DoWork);
myThread.Abort();
myThread.Start();
Process example:
using System;
using System.Diagnostics;
using System.ComponentModel;
using System.Threading;
using Microsoft.VisualBasic;
class PrintProcessClass
{
private Process myProcess = new Process();
private int elapsedTime;
private bool eventHandled;
// Print a file with any known extension.
public void PrintDoc(string fileName)
{
elapsedTime = 0;
eventHandled = false;
try
{
// Start a process to print a file and raise an event when done.
myProcess.StartInfo.FileName = fileName;
myProcess.StartInfo.Verb = "Print";
myProcess.StartInfo.CreateNoWindow = true;
myProcess.EnableRaisingEvents = true;
myProcess.Exited += new EventHandler(myProcess_Exited);
myProcess.Start();
}
catch (Exception ex)
{
Console.WriteLine("An error occurred trying to print \"{0}\":" + "\n" + ex.Message, fileName);
return;
}
// Wait for Exited event, but not more than 30 seconds.
const int SLEEP_AMOUNT = 100;
while (!eventHandled)
{
elapsedTime += SLEEP_AMOUNT;
if (elapsedTime > 30000)
{
break;
}
Thread.Sleep(SLEEP_AMOUNT);
}
}
// Handle Exited event and display process information.
private void myProcess_Exited(object sender, System.EventArgs e)
{
eventHandled = true;
Console.WriteLine("Exit time: {0}\r\n" +
"Exit code: {1}\r\nElapsed time: {2}", myProcess.ExitTime, myProcess.ExitCode, elapsedTime);
}
public static void Main(string[] args)
{
// Verify that an argument has been entered.
if (args.Length <= 0)
{
Console.WriteLine("Enter a file name.");
return;
}
// Create the process and print the document.
PrintProcessClass myPrintProcess = new PrintProcessClass();
myPrintProcess.PrintDoc(args[0]);
}
}