debugcsharpMinor
Waiting for a file to be accessible with Thread.Sleep() and exception catching
Viewed 0 times
fileexceptionwaitingaccessiblewithsleepcatchingthreadforand
Problem
I know I've already asked a similar question, but this feels quite different to me.
The main goal is to wait for a file to be accessible (read: waiting until the file lock releases), since it seems like there's no way to do this built into the .NET framework I've written a quick and dirty function to do this.
The requirements which should be kept in mind with this:
The problems I see myself:
Reimplementing this with a similar solution I already used smells like absolute overkill to me, given that this should be built-in functionality anyway. What stuff didn't I think about?
The main goal is to wait for a file to be accessible (read: waiting until the file lock releases), since it seems like there's no way to do this built into the .NET framework I've written a quick and dirty function to do this.
The requirements which should be kept in mind with this:
- The file shouldn't be locked often. Actually, it should be locked quite rarely (figure how often clients manage to still hit that).
- There's no need for a limit, if the file is locked for longer then a second, something is fuc^H^H^Hscrewed up.
- It should block the main thread until the file is available.
///
/// Tries to open the specified file over and over again until it is acessible.
///
public static FileStream WaitForStream(String filename, FileAccess fileAccess, FileShare, fileShare)
{
while(true) // Gives me the chills
{
try
{
return new FileStream(filename, FileMode.OpenOrCreate, fileAccess, fileShare);
}
catch (IOException ex)
{
Logger.LogError(ex); // Information purposes only
Threading.Thread.Sleep(250); // Just looks wrong
}
}
return null; // Only to keep the compiler happy.
}The problems I see myself:
while(true)
Thread.Sleep()
- It might loop forever, but as I said, if that happens we need to intervene anyway.
Reimplementing this with a similar solution I already used smells like absolute overkill to me, given that this should be built-in functionality anyway. What stuff didn't I think about?
Solution
How about making this an asynchrounous operation that only returns if the stream has successfully been opened. you can get rid of the while(true) and the thread sleep plus the new mthod call cn have a retry count.
Here is what it might look like if you use C# 4.0 (not tested).
if you still want to block the main thread, getting the Result property of the returning task will make sure of that
Here is what it might look like if you use C# 4.0 (not tested).
Task OpenStreamAsync(String filename, FileAccess fileAccess, FileShare fileShare,int retryCount){
if (retryCount new FileStream(filename, FileMode.OpenOrCreate, fileAccess, fileShare))
.ContinueWithTask( task => {
if (task.IsCompleted)
return task;
Logger.LogError(ex); // Information purposes only
return TaskExtenstion2.Wait(TimeSpan.FromMilliseconds(250))
.ContinueWithTask(t => OpenStreamAsync(filename, fileAccess, fileShare,retryCount--) );
});
}
public static class TaskExtenstion2 {
/// Set the completionSource to the same values as the task
public static void SetCompletionSource(this Task task, TaskCompletionSource completionSource){
if (task.IsCompleted){
completionSource.SetResult(task.Result);
}else if (task.IsCanceled){
completionSource.SetCanceled();
}else if (task.IsFaulted){
completionSource.SetException(task.Exception);
}
}
/// Continues a task with another task genrated by the specified function
public static Task ContinueWithTask(this Task sourceTask, Func,Task> continuation){
var completionSource = new TaskCompletionSource();
sourceTask.ContinueWith(firstTask => {
var secondTask = continuation(firstTask);
secondTask.ContinueWith(task => task.SetCompletionSource(completionSource));
});
return completionSource.Task;
}
/// returns true after a certain amount of time
public static Task Wait(TimeSpan span){
var completionSource = new TaskCompletionSource();
Timer timer = null;
timer = new Timer(_ => {
using(timer) {
completionSource.SetResult(true);
}
},null,span,TimeSpan.MaxValue);
return completionSource.Task;
}
}if you still want to block the main thread, getting the Result property of the returning task will make sure of that
var stream = OpenStreamAsync("myFile", FileAccess.ReadWrite, FileShare.None,20).Result;Code Snippets
Task<FileStream> OpenStreamAsync(String filename, FileAccess fileAccess, FileShare fileShare,int retryCount){
if (retryCount < 1)
throw new Exception("maximum retry reached");
return
Task.Factory.StartNew(() => new FileStream(filename, FileMode.OpenOrCreate, fileAccess, fileShare))
.ContinueWithTask( task => {
if (task.IsCompleted)
return task;
Logger.LogError(ex); // Information purposes only
return TaskExtenstion2.Wait(TimeSpan.FromMilliseconds(250))
.ContinueWithTask(t => OpenStreamAsync(filename, fileAccess, fileShare,retryCount--) );
});
}
public static class TaskExtenstion2 {
/// Set the completionSource to the same values as the task
public static void SetCompletionSource<TResult>(this Task<TResult> task, TaskCompletionSource<TResult> completionSource){
if (task.IsCompleted){
completionSource.SetResult(task.Result);
}else if (task.IsCanceled){
completionSource.SetCanceled();
}else if (task.IsFaulted){
completionSource.SetException(task.Exception);
}
}
/// Continues a task with another task genrated by the specified function
public static Task<U> ContinueWithTask<T,U>(this Task<T> sourceTask, Func<Task<T>,Task<U>> continuation){
var completionSource = new TaskCompletionSource<U>();
sourceTask.ContinueWith(firstTask => {
var secondTask = continuation(firstTask);
secondTask.ContinueWith(task => task.SetCompletionSource(completionSource));
});
return completionSource.Task;
}
/// returns true after a certain amount of time
public static Task<bool> Wait(TimeSpan span){
var completionSource = new TaskCompletionSource<bool>();
Timer timer = null;
timer = new Timer(_ => {
using(timer) {
completionSource.SetResult(true);
}
},null,span,TimeSpan.MaxValue);
return completionSource.Task;
}
}var stream = OpenStreamAsync("myFile", FileAccess.ReadWrite, FileShare.None,20).Result;Context
StackExchange Code Review Q#7210, answer score: 3
Revisions (0)
No revisions yet.