Почему жестко виснет нижеследующая мини-программа на .Net (C#)? Для проверки на C++ привожу 100% (?) аналог — он не виснет. А на C# виснет. В чем же тут причина?
PS. Настоящая проблема в том, что виснет при использовании стандартного System.IO.FileStream! Я упростил уж до CreateFile, что на C++, что на C#. Но что делать дальше, понять пока... а может и "уже", не могу
using System;
using System.Runtime.InteropServices;
using System.Threading;
namespace ConsoleApplication1
{
class Program
{
static IntPtr _handle;
static void Main()
{
_handle = CreateFile("test.txt", GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
new Thread(MyProc) { IsBackground = true }.Start();
new Thread(MyProc) { IsBackground = true }.Start();
Console.ReadKey();
}
static void MyProc()
{
for (int i = 0; i < int.MaxValue; i++)
{
Console.WriteLine(i);
var _overlapped = new NativeOverlapped();
LockFileEx(_handle, LOCKFILE_EXCLUSIVE_LOCK, 0, int.MaxValue, int.MaxValue, ref _overlapped);
Thread.Sleep(50);
UnlockFileEx(_handle, 0, int.MaxValue, int.MaxValue, ref _overlapped);
}
}
public const int LOCKFILE_EXCLUSIVE_LOCK = 0x00000002;
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool LockFileEx(IntPtr hFile, int flags, int reserved, int numberOfBytesToLockLow, int numberOfBytesToLockHigh,
ref NativeOverlapped overlapped);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool UnlockFileEx(IntPtr hFile, int reserved, int numberOfBytesToUnlockLow, int numberOfBytesToUnlockHigh,
ref NativeOverlapped overlapped);
public const int GENERIC_READ = unchecked((int)0x80000000L);
public const int GENERIC_WRITE = (int)0x40000000L;
public const int OPEN_ALWAYS = 4;
public const int FILE_SHARE_READ = 0x00000001;
public const int FILE_SHARE_WRITE = 0x00000002;
public const int FILE_SHARE_DELETE = 0x00000004;
public const int FILE_ATTRIBUTE_NORMAL = 0x00000080;
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr CreateFile(string fileName, int desiredAccess, int shareMode,
IntPtr pSecurityAttributes, int creationDisposition, int flagsAndAttributes, IntPtr hTemplateFile);
}
}
Здравствуйте, Fortnum, Вы писали:
F>Почему жестко виснет нижеследующая мини-программа на .Net (C#)? Для проверки на C++ привожу 100% (?) аналог — он не виснет. А на C# виснет. В чем же тут причина?
У меня не виснет. .NET 4.0.
/* иЗвиНите зА неРовнЫй поЧерК */
Re[2]: Почему .Net-версия жестко виснет, а C++ - нет?
Здравствуйте, hardcase, Вы писали:
F>>Почему жестко виснет нижеследующая мини-программа на .Net (C#)? Для проверки на C++ привожу 100% (?) аналог — он не виснет. А на C# виснет. В чем же тут причина? H>У меня не виснет. .NET 4.0.
У меня .Net 4.5, VS 2012 Express, Win7 64bit, i7-3610QM — виснет как черт! Показывает два нуля и висит. Иногда, крайне-крайне редко доходит до двух десяток. Кто еще протестировал, отпишитесь. Конфигурацию укажите, пожалуйста.
Re: Почему .Net-версия жестко виснет, а C++ - нет?
Здравствуйте, Fortnum, Вы писали:
F>Почему жестко виснет нижеследующая мини-программа на .Net (C#)? Для проверки на C++ привожу 100% (?) аналог — он не виснет. А на C# виснет. В чем же тут причина?
Функции LockFileEx и UnlockFileEx возвращают BOOL, который typedef для int. Мне кажется, что bool из C# в объявлении функции некорректен.
Далее, возвращаемое значение надо обязательно проверять на неравенство FALSE (0 то бишь). А результат CreateFile надо обязательно проверять на неравенство INVALID_HANDLE_VALUE.
Далее, диапазоны в LockFileEx и UnlockFileEx задаются беззнаковыми int.
Вообще мне кажется, что файл должен укладываться в этот диапазон, т.е. чтобы заблокировать весь файл надо передавать туда его размер, а не просто максимально возможное значение.
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Re[2]: Почему .Net-версия жестко виснет, а C++ - нет?
Здравствуйте, VTT, Вы писали:
VTT>Функции LockFileEx и UnlockFileEx возвращают BOOL, который typedef для int. Мне кажется, что bool из C# в объявлении функции некорректен.
Не могу ничем подтвердить с ходу, но маршаллинг нормально обрабатывает эту конвертацию.
VTT>Далее, возвращаемое значение надо обязательно проверять на неравенство FALSE (0 то бишь). А результат CreateFile надо обязательно проверять на неравенство INVALID_HANDLE_VALUE.
В рабочей программе проверяю результат LockFileEx и UnlockFileEx. Всегда возвращается успех. Висит, кстати, в 99.9% случаях в таком положении, что поток 1 на LockFileEx, а поток 2 на UnlockFileEx. И все — deadlock. Если внутри использовать StreamWriter (тут уж без CreateFile — в рабочей программе проблема с FileStream'ом), то один из потоков на LockFileEx, а другой где-то в конструкторе StreamWriter'а. Было так один раз всего, повторить не удалось еще.
VTT>Далее, диапазоны в LockFileEx и UnlockFileEx задаются беззнаковыми int.
Я разделяю файл с другой программой, которой нужен именно такой диапазон — в беззнаковый, главное, он вписывается.
VTT>Вообще мне кажется, что файл должен укладываться в этот диапазон, т.е. чтобы заблокировать весь файл надо передавать туда его размер, а не просто максимально возможное значение.
Здравствуйте, Fortnum, Вы писали:
F>У меня .Net 4.5, VS 2012 Express, Win7 64bit, i7-3610QM — виснет как черт! Показывает два нуля и висит. Иногда, крайне-крайне редко доходит до двух десяток. Кто еще протестировал, отпишитесь.
Нормально дошла до 150, прервал.
>Конфигурацию укажите, пожалуйста.
VS 2013, Debug. .NET 4.5, AnyCPU, Windows 7 x64
With best regards
Pavel Dvorkin
Re[4]: Почему .Net-версия жестко виснет, а C++ - нет?
Здравствуйте, Pavel Dvorkin, Вы писали:
F>>У меня .Net 4.5, VS 2012 Express, Win7 64bit, i7-3610QM — виснет как черт! Показывает два нуля и висит. Иногда, крайне-крайне редко доходит до двух десяток. Кто еще протестировал, отпишитесь. PD>Нормально дошла до 150, прервал.
Тут чудо какое-то чудное! Ради прикола стал менять диапазон у LockFileEx/UnlockFileEx, по ходу сделал Rebuild, удалил текстовый файл, и... оно заработало! Но!.. Поменял диапазон, и оно перестало работать. Потом удалил файл, оно снова заработало. Второй раз запустил, оно повисло на 10-ке. Теперь не работает капитально (C# версия). В общем, вот, screencast записал аж: http://www.screencast.com/users/frtnum/folders/Jing/media/caf86150-5a52-448e-a8cd-9824c7ffbbb4
Re[5]: Почему .Net-версия жестко виснет, а C++ - нет?
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Посмотрел еще раз. А что, собственно говоря, ты хочешь сделать ? PD>Locks the specified file for exclusive access by the calling process. PD>https://msdn.microsoft.com/en-us/library/windows/desktop/aa365203%28v=vs.85%29.aspx PD>Функция блокирует регион файла для процесса. При чем тут потоки одного процесса ?
А действительно!..
PD>И оттуда же PD>Exclusive locks cannot overlap an existing locked region of a file PD>А ты именно это и делаешь. PD>В общем, распечатай результат LockFileEx и при false распечатай GetLastError.
Вот так изменил MyProc, время нахождения в lock'е увеличил до секунды, чтобы потоки наверняка пересекались... и, блин, оно заработало! Возвращает TRUE, хотя работать-то, получается, не должно? Ничего не понимаю
static void MyProc()
{
for (int i = 0; i < int.MaxValue; i++)
{
Console.WriteLine(i);
var _overlapped = new NativeOverlapped();
var r1 = LockFileEx(_handle, LOCKFILE_EXCLUSIVE_LOCK, 0, int.MaxValue, int.MaxValue, ref _overlapped);
Console.WriteLine("{0}>r1={1}", Thread.CurrentThread.ManagedThreadId, r1);
Thread.Sleep(1000);
var r2 = UnlockFileEx(_handle, 0, int.MaxValue, int.MaxValue, ref _overlapped);
Console.WriteLine("{0}>r2={1}", Thread.CurrentThread.ManagedThreadId, r2);
}
}
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Посмотрел еще раз. А что, собственно говоря, ты хочешь сделать ? PD>Locks the specified file for exclusive access by the calling process. PD>https://msdn.microsoft.com/en-us/library/windows/desktop/aa365203%28v=vs.85%29.aspx PD>Функция блокирует регион файла для процесса. При чем тут потоки одного процесса ?
С другой стороны, какая ОС разница, разные потоки или разные процессы? Там даже такая фраза: " If the locking process opens the file a second time, it cannot access the specified region through this second handle until it unlocks the region.".
Хендл один и тот же — это да, вопрос... но тут не все однозначно в документации!
PD>И оттуда же PD>Exclusive locks cannot overlap an existing locked region of a file PD>А ты именно это и делаешь.
В том-то и дело, что под этим следует понимать, если они ovelap'ятся, поток уходит в сон, пока запрашиваемый ресурс не освободится. Ведь не написано "Exclusive locks of the same handle cannot...". То есть оно так и задумано, чтоб LockFileEx вызывался на один и тот же диапазон у одного и того же файла.
Если разные хендлы использовать, работать официально на 100% должно. С другой стороны, где явный запрет на множественные вызовы LockFileEx c одним хендлом?
Более того, там вот как сказано: "A shared lock can overlap an exclusive lock if both locks were created using the same file handle. When a shared lock overlaps an exclusive lock, the only possible access is a read by the owner of the locks".
Попробовал с разными хендлами на один файл — вроде работает Но мне лучше будет синхронизацию внутри процесса сделать, т.к. я файл открываю в начале процесса, и закрываю с его концом — блокирую лишь на время записи. Думал халявную синхронизацию использовать Однако, поведение оказалось слишком неоднозначным. Кидало бы ошибку — нет, у всех кроме меня — работает! Почему я?
Re[7]: Почему .Net-версия жестко виснет, а C++ - нет?
Здравствуйте, Fortnum, Вы писали:
F>Вот так изменил MyProc, время нахождения в lock'е увеличил до секунды, чтобы потоки наверняка пересекались... и, блин, оно заработало! Возвращает TRUE, хотя работать-то, получается, не должно? Ничего не понимаю
Похоже, ты не первый, кто с этой проблемой столкнулся.
странную, на первый взгляд, весчь предположу, но вот в настройках C# проекта на вкладке Debug есть галочка "Enable the Visual Studio h&osting process".
она у при опытах снята или нет?
Re[2]: Почему .Net-версия жестко виснет, а C++ - нет?
Здравствуйте, Igorxz, Вы писали:
I>странную, на первый взгляд, весчь предположу, но вот в настройках C# проекта на вкладке Debug есть галочка "Enable the Visual Studio h&osting process". I>она у при опытах снята или нет?
И так и так пробовал Не оказывало влияния. На самом деле, проблема в повторном вызове из разных потоков LockFileEx(LOCKFILE_EXCLUSIVE_LOCK) над одним и тем же хендлом файла. Точнее, проблема в том, что в документации на LockFileEx такое четкое требование отсутствует, а ошибка проявляется очень редко вероятностно, вместо детерминированной ошибки. Более того, такое впечатление, что у всех кроме меня работает (да и у меня случайным образом оно работает). Рабочий проект тоже работал до поры до времени
Re[3]: Почему .Net-версия жестко виснет, а C++ - нет?
Здравствуйте, Fortnum, Вы писали:
F>Здравствуйте, Igorxz, Вы писали:
I>>странную, на первый взгляд, весчь предположу, но вот в настройках C# проекта на вкладке Debug есть галочка "Enable the Visual Studio h&osting process". I>>она у при опытах снята или нет?
F>И так и так пробовал Не оказывало влияния. На самом деле, проблема в повторном вызове из разных потоков LockFileEx(LOCKFILE_EXCLUSIVE_LOCK) над одним и тем же хендлом файла. Точнее, проблема в том, что в документации на LockFileEx такое четкое требование отсутствует, а ошибка проявляется очень редко вероятностно, вместо детерминированной ошибки. Более того, такое впечатление, что у всех кроме меня работает (да и у меня случайным образом оно работает). Рабочий проект тоже работал до поры до времени
отличие работы C\C++ версии от C#-ной думаю именно в етом
Re[4]: Почему .Net-версия жестко виснет, а C++ - нет?
Здравствуйте, Igorxz, Вы писали:
F>>И так и так пробовал Не оказывало влияния. На самом деле, проблема в повторном вызове из разных потоков LockFileEx(LOCKFILE_EXCLUSIVE_LOCK) над одним и тем же хендлом файла. Точнее, проблема в том, что в документации на LockFileEx такое четкое требование отсутствует, а ошибка проявляется очень редко вероятностно, вместо детерминированной ошибки. Более того, такое впечатление, что у всех кроме меня работает (да и у меня случайным образом оно работает). Рабочий проект тоже работал до поры до времени I>отличие работы C\C++ версии от C#-ной думаю именно в етом
Я, кстати, тут в теме съемку экрана выкладывал http://www.screencast.com/users/frtnum/folders/Jing/media/caf86150-5a52-448e-a8cd-9824c7ffbbb4, там видно, что когда первый раз C# версию после удаления файла стартуешь, счетчик на консоли доходит до десятки. А когда второй раз стартуешь, по уже созданному файлу, deadlock стабильно сразу встречается, на нулевом значении счетчика. Вообще, непонятно, от чего это зависит. От таймингов каких-то что ли?..