С традиционным для цикла
ArrayList<String> myArray = new ArrayList<>();
for (int i = 0; i < myArray.size(); ) {
String text = myArray.get(i);
if (someCondition(text))
myArray.remove(i);
else
i++;
}
Это было ответом, который я дал на связанный вопрос :
/// <summary>
/// Blocks until the file is not locked any more.
/// </summary>
/// <param name="fullPath"></param>
bool WaitForFile(string fullPath)
{
int numTries = 0;
while (true)
{
++numTries;
try
{
// Attempt to open the file exclusively.
using (FileStream fs = new FileStream(fullPath,
FileMode.Open, FileAccess.ReadWrite,
FileShare.None, 100))
{
fs.ReadByte();
// If we got this far the file is ready
break;
}
}
catch (Exception ex)
{
Log.LogWarning(
"WaitForFile {0} failed to get an exclusive lock: {1}",
fullPath, ex.ToString());
if (numTries > 10)
{
Log.LogWarning(
"WaitForFile {0} giving up after 10 tries",
fullPath);
return false;
}
// Wait for the lock to be released
System.Threading.Thread.Sleep(500);
}
}
Log.LogTrace("WaitForFile {0} returning true after {1} tries",
fullPath, numTries);
return true;
}
Я делаю это тот же путь как Gulzar, просто продолжаю пробовать циклом.
На самом деле я даже не беспокоюсь наблюдателем файловой системы. При опросе сетевого диска относительно новых файлов, как только минута является дешевой.
Я не знаю то, что Вы используете для определения состояния блокировки файла, но что-то вроде этого должно сделать это.
while (true) { try { stream = File.Open( fileName, fileMode ); break; } catch( FileIOException ) { // check whether it's a lock problem Thread.Sleep( 100 ); } }
От MSDN:
событие OnCreated генерируется, как только файл создается. Если файл будет скопирован или передан в наблюдаемый каталог, событие OnCreated будет сразу сгенерировано, сопровождаться одним или несколькими событиями OnChanged.
Ваш FileSystemWatcher мог быть изменен так, чтобы он не делал читать/переименовывать во время события "OnCreated", а скорее:
Один из методов, я использовал некоторое время назад, должен был записать мою собственную функцию. В основном поймайте исключение и повторную попытку с помощью таймера, который можно уволить за указанную продолжительность. Если существует лучший путь, совместно используйте.
Для этого конкретного приложения, непосредственно наблюдая файл неизбежно приведет к твердому для трассировки ошибки, особенно когда размер файла увеличится. Вот две различных стратегии, которые будут работать.
Удачи!
В большинстве случаев простой подход как предложенный @harpo будет работать. Можно разработать более сложный код с помощью этого подхода:
Как насчет этого в качестве опции:
private void WaitOnFile(string fileName)
{
FileInfo fileInfo = new FileInfo(fileName);
for (long size = -1; size != fileInfo.Length; fileInfo.Refresh())
{
size = fileInfo.Length;
System.Threading.Thread.Sleep(1000);
}
}
Конечно, если размер файла будет предварительно выделен при создании, вы получите ложное срабатывание.
Я собрал вспомогательный класс для такого рода вещей. Это сработает, если вы контролируете все, что имеет доступ к файлу. Если вы ожидаете разногласий от множества других вещей, то это бесполезно.
using System;
using System.IO;
using System.Threading;
/// <summary>
/// This is a wrapper aroung a FileStream. While it is not a Stream itself, it can be cast to
/// one (keep in mind that this might throw an exception).
/// </summary>
public class SafeFileStream: IDisposable
{
#region Private Members
private Mutex m_mutex;
private Stream m_stream;
private string m_path;
private FileMode m_fileMode;
private FileAccess m_fileAccess;
private FileShare m_fileShare;
#endregion//Private Members
#region Constructors
public SafeFileStream(string path, FileMode mode, FileAccess access, FileShare share)
{
m_mutex = new Mutex(false, String.Format("Global\\{0}", path.Replace('\\', '/')));
m_path = path;
m_fileMode = mode;
m_fileAccess = access;
m_fileShare = share;
}
#endregion//Constructors
#region Properties
public Stream UnderlyingStream
{
get
{
if (!IsOpen)
throw new InvalidOperationException("The underlying stream does not exist - try opening this stream.");
return m_stream;
}
}
public bool IsOpen
{
get { return m_stream != null; }
}
#endregion//Properties
#region Functions
/// <summary>
/// Opens the stream when it is not locked. If the file is locked, then
/// </summary>
public void Open()
{
if (m_stream != null)
throw new InvalidOperationException(SafeFileResources.FileOpenExceptionMessage);
m_mutex.WaitOne();
m_stream = File.Open(m_path, m_fileMode, m_fileAccess, m_fileShare);
}
public bool TryOpen(TimeSpan span)
{
if (m_stream != null)
throw new InvalidOperationException(SafeFileResources.FileOpenExceptionMessage);
if (m_mutex.WaitOne(span))
{
m_stream = File.Open(m_path, m_fileMode, m_fileAccess, m_fileShare);
return true;
}
else
return false;
}
public void Close()
{
if (m_stream != null)
{
m_stream.Close();
m_stream = null;
m_mutex.ReleaseMutex();
}
}
public void Dispose()
{
Close();
GC.SuppressFinalize(this);
}
public static explicit operator Stream(SafeFileStream sfs)
{
return sfs.UnderlyingStream;
}
#endregion//Functions
}
Он работает с использованием именованного мьютекса. Те, кто желает получить доступ к файлу, пытаются получить контроль над именованным мьютексом, который разделяет имя файла (с замененными '\' на '/'). Вы можете использовать Open (), который будет останавливаться до тех пор, пока мьютекс не станет доступным, или вы можете использовать TryOpen (TimeSpan), который пытается получить мьютекс на заданное время и возвращает false, если он не может быть получен в течение указанного промежутка времени. Это, скорее всего, следует использовать внутри блока using, чтобы гарантировать, что блокировки сняты должным образом, и поток (если он открыт) будет правильно удален, когда этот объект будет удален.
Я провел быстрый тест с ~ 20 действиями для выполнения различных операций чтения / записи файла и не обнаружил повреждений. Очевидно, что он не очень продвинутый, но он должен работать в большинстве простых случаев.