IOException: процесс не может получить доступ к файлу "путь к файлу", потому что он используется другим процессом


У меня есть некоторый код, и когда он выполняет, он бросает!--0-->, сказав, что

процесс не может получить доступ к файлу "имя_файла", потому что он используется другой процесс

что это значит, и что я могу поделать?

6 124

6 ответов:

в чем причина?

сообщение об ошибке довольно ясно: вы пытаетесь получить доступ к файлу, и он недоступен, потому что другой процесс (или даже тот же процесс) делает что-то с ним (и он не разрешал никакого совместного использования).

отладка

это может быть довольно легко решить (или очень трудно понять), в зависимости от конкретного сценария. Давайте посмотрим некоторые.

ваш процесс является единственным для доступа к файл
Вы уверены, что другое процесс-это ваш собственный процесс. Если вы знаете, что открываете этот файл в другой части своей программы, то прежде всего вам нужно проверить, правильно ли вы закрываете дескриптор файла после каждого использования. Вот пример кода с этой ошибкой:

var stream = new FileStream(path, FileAccess.Read);
var reader = new StreamReader(stream);
// Read data from this file, when I'm done I don't need it any more
File.Delete(path); // IOException: file is in use

к счастью FileStream осуществляет IDisposable, так что это легко обернуть весь ваш код внутри using о себе:

using (var stream = File.Open("myfile.txt", FileMode.Open)) {
    // Use stream
}

// Here stream is not accessible and it has been closed (also if
// an exception is thrown and stack unrolled

этот шаблон также гарантирует, что файл не будет оставлен открытым в случае исключений (это может быть причиной использования файла: что-то пошло не так, и никто его не закрыл; см. этот пост для примера).

если все кажется прекрасным (вы уверены, что всегда закрываете каждый открытый файл, даже в случае исключений), и у вас есть несколько рабочих потоков, то у вас есть два варианта: переработать код для сериализации доступа к файлам (не всегда выполнимо и не всегда требуется) или применить шаблон повтора. Это довольно распространенный шаблон для операций ввода-вывода: вы пытаетесь что-то сделать, и в случае ошибки вы ждете и пытаетесь снова (вы спрашивали себя, почему, например, оболочка Windows занимает некоторое время, чтобы сообщить вам, что файл используется и не может быть удален?). В C# это довольно легко реализовать (см. также лучшие примеры о дисковых операций ввода-вывода,сеть и доступ к базе данных).

private const int NumberOfRetries = 3;
private const int DelayOnRetry = 1000;

for (int i=1; i <= NumberOfRetries; ++i) {
    try {
        // Do stuff with file
        break; // When done we can break loop
    }
    catch (IOException e) when (i <= NumberOfRetries) {
        // You may check error code to filter some exceptions, not every error
        // can be recovered.
        Thread.Sleep(DelayOnRetry);
    }
}

обратите внимание на распространенную ошибку, которую мы видим очень часто StackOverflow:

var stream = File.Open(path, FileOpen.Read);
var content = File.ReadAllText(path);

в этом случае ReadAllText() не получится, потому что файл используется (File.Open() в строке до). Открывать файл заранее не только не нужно, но и неправильно. То же самое относится ко всем File функции, которые не возвращают дескриптор в файл, с которым вы работаете:File.ReadAllText(),File.WriteAllText(),File.ReadAllLines(),File.WriteAllLines() и другие (например,File.AppendAllXyz() функции) все откроют и закроют файл сами по себе.

ваш процесс это не единственный доступ к этому файлу
Если ваш процесс не только получить доступ к этому файлу, то взаимодействие может быть сложнее. А шаблон повтора поможет (если файл не должен быть открыт кем-либо еще, но это так, то вам нужна утилита, такая как Process Explorer, чтобы проверить кто делает что).

способов избежать

если применимо, всегда используйте используя заявления для открытия файлов. Как сказано в предыдущем абзаце он активно поможет вам избежать многих распространенных ошибок (см. этот пост пример как не использовать).

если возможно, попробуйте решить, кому принадлежит доступ к конкретному файлу и централизовать доступ с помощью нескольких известных методов. Если, например, у вас есть файл данных, где ваша программа читает и записывает, то вы должны поместить весь код ввода-вывода внутри одного класса. Это упростит отладку (потому что вы всегда можете поставить точку останова там и посмотреть, кто что делает), а также это будет точка синхронизации (если требуется) для множественного доступа.

не забывайте, что операции ввода-вывода всегда может выйти из строя, например это:

if (File.Exists(path))
    File.Delete(path);

если кто-то удаляет файл после File.Exists() но до File.Delete(), тогда он бросит IOException в месте, где вы можете ошибочно чувствовать себя в безопасности.

всякий раз, когда это возможно, применять шаблон повтора, и если вы используете FileSystemWatcher, подумайте о переносе действия (потому что вы получите уведомление, но приложение все еще может работать исключительно с этим файлом).

дополнительные сценарии
Это не всегда так просто, поэтому вам может потребоваться поделиться доступом с кем-то другим. Если, например, Вы читаете с начала и пишете до конца, у вас есть по крайней мере два варианта.

1) доля же FileStream с правильными функциями синхронизации (потому что это не потокобезопасный). Смотрите этой и этой сообщения для примера.

2) использовать FileShare перечисление для указания ОС разрешить другим процессам (или другим частям вашего собственного процесса) одновременно обращаться к одному и тому же файлу.

using (var stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.Read))
{
}

в этом примере я показал, как открыть файл для записи и Поделиться для чтения; обратите внимание, что при чтении и записи перекрывается, это приводит к неопределенным или недопустимым данным. Это ситуация, которая должна быть решена при чтении. Также обратите внимание, что это не делает доступ к stream потокобезопасный, поэтому этот объект не может быть совместно использован с несколькими потоками, если доступ не синхронизирован каким-либо образом (см. предыдущие ссылки). Доступны другие параметры общего доступа, и они открывают более сложные сценарии. Пожалуйста, обратитесь к MSDN для более подробной информации.

в общем N процессы могут читать из одного файла все вместе, но только один должен писать, в контролируемом сценарии вы можете даже включите параллельные записи, но это не может быть обобщено в нескольких текстовых абзацах внутри этого ответа.

можно разблокировать файл, используемый другим процессом? Это не всегда безопасно и не так просто, Но да,вполне возможно.

С помощью FileShare Исправлена проблема открытия файла, даже если он открыт другим процессом.

using (var stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.ReadWrite))
{
}

возникла проблема при загрузке изображения и не удалось удалить его и найти решение. гл ХФ

//C# .NET
var image = Image.FromFile(filePath);

image.Dispose(); // this removes all resources

//later...

File.Delete(filePath); //now works

Я получил эту ошибку, потому что я делал файл.Перейти к пути к файлу без имени файла, необходимо указать полный путь в пункте назначения.

У меня был следующий случай, который вызывает ту же ошибку:

  • загрузить файлы на сервер
  • затем избавиться от старых файлов после того как они были загружены

большинство файлов были небольшими по размеру, однако некоторые из них были большими, и поэтому попытка удалить их привела к не удается получить доступ к файлу ошибка.

Это было нелегко найти, однако, решение было так же просто, как ожидание "для выполнения задачи для завершения исполнения":

using (var wc = new WebClient())
{
   var tskResult = wc.UploadFileTaskAsync(_address, _fileName);
   tskResult.Wait(); 
}

как указывали другие ответы в этом потоке, чтобы устранить эту ошибку, вам нужно тщательно проверить код, чтобы понять, где файл блокируется.

в моем случае я отправлял файл в виде вложения электронной почты перед выполнением операции перемещения.

таким образом, файл был заблокирован на пару секунд, пока SMTP-клиент не закончил отправку электронной почты.

решение, которое я принял было сначала переместите файл, а затем отправьте электронная почта. это решило проблему для меня.

другим возможным решением, как указывал ранее Хадсон, было бы утилизировать объект после использования.

public static SendEmail()
{
           MailMessage mMailMessage = new MailMessage();
           //setup other email stuff

            if (File.Exists(attachmentPath))
            {
                Attachment attachment = new Attachment(attachmentPath);
                mMailMessage.Attachments.Add(attachment);
                attachment.Dispose(); //disposing the Attachment object
            }
}