Wait until file is unlocked in .NET
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
In .NET, waiting for a file to become unlocked is usually really a question of how to acquire the file handle safely when another process might still be using it. The important design point is that a separate “is it unlocked yet” check is race-prone, so the safest pattern is usually to retry the real open operation and keep the handle you successfully obtain.
Avoid a Separate Check Step
A method such as IsFileUnlocked(path) looks attractive, but it creates a race. Even if the check says the file is free, another process can grab the file before your next line opens it.
That is why the better abstraction is not “wait until unlocked.” It is “open when available.” If the open succeeds, you already own the handle that matters.
Retry the Real Open
The usual .NET signal for lock contention is an IOException when creating a FileStream. A bounded retry loop is a practical solution.
The key feature is that the method returns the FileStream itself. That means the successful open and the later work are the same operation.
Use the Acquired Handle
Once the helper returns a stream, do the work through that stream instead of reopening the file later.
If you reopen the file after the helper returns, you bring the race back.
Timeouts and Cancellation Matter
If another process never releases the file, waiting forever is usually the wrong outcome. A timeout is part of the API contract, not an optional extra.
Cancellation matters for the same reason. If the calling workflow is shutting down or the user has cancelled the operation, the retry loop should stop promptly instead of continuing to poll pointlessly.
This makes the helper usable in long-running services and UI applications, not just in throwaway scripts.
Not Every IOException Is a Lock Problem
One subtle point is that IOException can happen for reasons other than file locks. The path might not exist yet, a directory could be missing, or the process might not have permission to access the file.
If your workflow needs better diagnostics, narrow the conditions you retry or log the exact context of the failure. A blind “retry all I O errors forever” loop can hide real bugs.
Sometimes Waiting Is the Wrong Design
If your application regularly waits on another process to finish writing files, the file lock may be the wrong coordination mechanism. In many systems, it is cleaner to:
- write to a temporary file and rename when complete
- create a separate “ready” marker file
- coordinate through an application-level queue or protocol
Those designs are often easier to reason about than using lock contention as normal control flow.
Scope Your Own File Handles Correctly
A surprising number of “wait until unlocked” problems come from your own code forgetting to dispose a stream. If you opened the file earlier and did not close it promptly, no amount of retry logic fixes the underlying ownership bug.
That is why using and await using are still the first tools to reach for. Retry logic should handle external contention, not compensate for sloppy stream lifetime management.
Common Pitfalls
- Checking whether the file is unlocked and then reopening it later, which creates a race.
- Retrying forever without a timeout or cancellation path.
- Catching every
IOExceptionand assuming the only cause is a lock. - Discarding the successfully opened handle and reopening the file again.
- Using lock contention as an application protocol when a ready signal would be clearer.
Summary
- In .NET, the safest pattern is usually “open when available,” not “check if unlocked.”
- Retry the actual
FileStreamopen with a bounded delay and timeout. - Keep and use the handle you successfully acquired.
- Add cancellation and meaningful diagnostics so failures are debuggable.
- If waiting is routine, consider redesigning the workflow instead of treating file locks as normal coordination.

