Почему .Net-версия жестко виснет, а C++ - нет?
От: Fortnum  
Дата: 17.05.15 09:48
Оценка:
Почему жестко виснет нижеследующая мини-программа на .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);
    }
}


#include "stdafx.h"

#include <thread>
#include <Windows.h>

HANDLE hFile;

DWORD WINAPI MyProc(LPVOID lpParam);

int _tmain(int argc, _TCHAR* argv[])
{
    hFile = CreateFile(L"test.txt", GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
        OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);    

    HANDLE hThread1 = CreateThread(NULL, 0, MyProc, NULL, 0, NULL);
    HANDLE hThread2 = CreateThread(NULL, 0, MyProc, NULL, 0, NULL);

    int ch = getchar();

    return 0;
}

DWORD WINAPI MyProc(LPVOID lpParam)
{
    for(int i = 0; i < INT_MAX; i++)
    {
        printf("%d\r\n", i);

        OVERLAPPED overlapped = {0};

        LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, INT_MAX, INT_MAX, &overlapped);

        Sleep(50);

        UnlockFileEx(hFile, 0, INT_MAX, INT_MAX, &overlapped);
    }

    return 0;
}
Отредактировано 17.05.2015 11:09 fortnum . Предыдущая версия .
Re: Почему .Net-версия жестко виснет, а C++ - нет?
От: hardcase Пират http://nemerle.org
Дата: 17.05.15 10:38
Оценка:
Здравствуйте, Fortnum, Вы писали:

F>Почему жестко виснет нижеследующая мини-программа на .Net (C#)? Для проверки на C++ привожу 100% (?) аналог — он не виснет. А на C# виснет. В чем же тут причина?


У меня не виснет. .NET 4.0.
/* иЗвиНите зА неРовнЫй поЧерК */
Re[2]: Почему .Net-версия жестко виснет, а C++ - нет?
От: Fortnum  
Дата: 17.05.15 10:42
Оценка:
Здравствуйте, hardcase, Вы писали:

F>>Почему жестко виснет нижеследующая мини-программа на .Net (C#)? Для проверки на C++ привожу 100% (?) аналог — он не виснет. А на C# виснет. В чем же тут причина?

H>У меня не виснет. .NET 4.0.

У меня .Net 4.5, VS 2012 Express, Win7 64bit, i7-3610QM — виснет как черт! Показывает два нуля и висит. Иногда, крайне-крайне редко доходит до двух десяток. Кто еще протестировал, отпишитесь. Конфигурацию укажите, пожалуйста.
Re: Почему .Net-версия жестко виснет, а C++ - нет?
От: VTT http://vtt.to
Дата: 17.05.15 10:46
Оценка:
Здравствуйте, 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++ - нет?
От: Fortnum  
Дата: 17.05.15 10:56
Оценка:
Здравствуйте, 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>Вообще мне кажется, что файл должен укладываться в этот диапазон, т.е. чтобы заблокировать весь файл надо передавать туда его размер, а не просто максимально возможное значение.


https://msdn.microsoft.com/en-us/library/windows/desktop/aa365202(v=vs.85).aspx

You can lock bytes that are beyond the end of the current file. This is useful to coordinate adding records to the end of a file.

Re[3]: Почему .Net-версия жестко виснет, а C++ - нет?
От: Pavel Dvorkin Россия  
Дата: 17.05.15 11:03
Оценка:
Здравствуйте, 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++ - нет?
От: Fortnum  
Дата: 17.05.15 12:01
Оценка:
Здравствуйте, 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 Россия  
Дата: 17.05.15 12:18
Оценка: 6 (1)
Здравствуйте, Fortnum, Вы писали:

Посмотрел еще раз. А что, собственно говоря, ты хочешь сделать ?

Locks the specified file for exclusive access by the calling process.

https://msdn.microsoft.com/en-us/library/windows/desktop/aa365203%28v=vs.85%29.aspx

Функция блокирует регион файла для процесса. При чем тут потоки одного процесса ?

И оттуда же

Exclusive locks cannot overlap an existing locked region of a file

А ты именно это и делаешь.

В общем, распечатай результат LockFileEx и при false распечатай GetLastError.
With best regards
Pavel Dvorkin
Re[6]: Почему .Net-версия жестко виснет, а C++ - нет?
От: Fortnum  
Дата: 17.05.15 12:31
Оценка:
Здравствуйте, 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);
    }
}


Результат:

0
0
3>r1=True
3>r2=True
1
4>r1=True
4>r2=True
1
3>r1=True
3>r2=True
2
4>r1=True
4>r2=True
2
3>r1=True
3>r2=True
3
4>r1=True
4>r2=True
3
3>r1=True
3>r2=True
4
4>r1=True
4>r2=True
4
3>r1=True
3>r2=True
5
4>r1=True
4>r2=True
5
3>r1=True
3>r2=True
6
4>r1=True
4>r2=True
6
3>r1=True
3>r2=True
7
4>r1=True
4>r2=True
7
3>r1=True
3>r2=True
8
4>r1=True
4>r2=True
8
3>r1=True
3>r2=True
9
4>r1=True
4>r2=True
9
3>r1=True
3>r2=True
10
4>r1=True
4>r2=True
10
3>r1=True
3>r2=True
11
4>r1=True
4>r2=True
11
3>r1=True
3>r2=True
12
4>r1=True
4>r2=True
12
3>r1=True
3>r2=True
13
4>r1=True
4>r2=True
13
3>r1=True
3>r2=True
14
4>r1=True
4>r2=True
14
3>r1=True
3>r2=True
15
4>r1=True
4>r2=True
15
3>r1=True
3>r2=True
16
4>r1=True
4>r2=True
16
3>r1=True
3>r2=True
17
4>r1=True
4>r2=True
17
3>r1=True
3>r2=True
18
4>r1=True
4>r2=True
18
3>r1=True
3>r2=True
19
4>r1=True
4>r2=True
19
3>r1=True
3>r2=True
20
4>r1=True
4>r2=True
20
3>r1=True
3>r2=True
21
4>r1=True
4>r2=True
21
3>r1=True
3>r2=True
22
4>r1=True
4>r2=True
22
3>r1=True
3>r2=True
23
4>r1=True
4>r2=True
23
3>r1=True
3>r2=True
24
4>r1=True
4>r2=True
24
3>r1=True
3>r2=True
25
4>r1=True
4>r2=True
3>r1=True
25
3>r2=True
26
4>r1=True
4>r2=True
26
3>r1=True
3>r2=True
27
4>r1=True
4>r2=True
27
3>r1=True
3>r2=True
28
4>r1=True
4>r2=True
28
3>r1=True
3>r2=True
29
4>r1=True
4>r2=True
29
3>r1=True
3>r2=True
30
4>r1=True
4>r2=True
30
3>r1=True
3>r2=True
31
4>r1=True
4>r2=True
31
3>r1=True
3>r2=True
32
4>r1=True
4>r2=True
32
3>r1=True
3>r2=True
33
4>r1=True
4>r2=True
33
3>r1=True
3>r2=True
34
4>r1=True
4>r2=True
34
3>r1=True

Re[6]: Почему .Net-версия жестко виснет, а C++ - нет?
От: Fortnum  
Дата: 17.05.15 12:47
Оценка:
Здравствуйте, 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++ - нет?
От: Pavel Dvorkin Россия  
Дата: 17.05.15 12:50
Оценка: 68 (2)
Здравствуйте, Fortnum, Вы писали:

F>Вот так изменил MyProc, время нахождения в lock'е увеличил до секунды, чтобы потоки наверняка пересекались... и, блин, оно заработало! Возвращает TRUE, хотя работать-то, получается, не должно? Ничего не понимаю


Похоже, ты не первый, кто с этой проблемой столкнулся.

http://stackoverflow.com/questions/23598531/deadlock-using-lockfile

Вот здесь есть такое замечание

http://marc.info/?l=apr-dev&amp;m=138244402421112&amp;w=2

"because the LockFileEx() should not be called recursively."

recursively или в разных потоках — в данном случае одно и то же.

Хотя про официальный запрет рекурсии я что-то ничего найти не могу.
With best regards
Pavel Dvorkin
Отредактировано 17.05.2015 12:52 Pavel Dvorkin . Предыдущая версия .
Re: Почему .Net-версия жестко виснет, а C++ - нет?
От: Igorxz  
Дата: 17.05.15 17:20
Оценка:
Здравствуйте, Fortnum, Вы писали:

странную, на первый взгляд, весчь предположу, но вот в настройках C# проекта на вкладке Debug есть галочка "Enable the Visual Studio h&osting process".
она у при опытах снята или нет?
Re[2]: Почему .Net-версия жестко виснет, а C++ - нет?
От: Fortnum  
Дата: 17.05.15 18:00
Оценка:
Здравствуйте, Igorxz, Вы писали:

I>странную, на первый взгляд, весчь предположу, но вот в настройках C# проекта на вкладке Debug есть галочка "Enable the Visual Studio h&osting process".

I>она у при опытах снята или нет?

И так и так пробовал Не оказывало влияния. На самом деле, проблема в повторном вызове из разных потоков LockFileEx(LOCKFILE_EXCLUSIVE_LOCK) над одним и тем же хендлом файла. Точнее, проблема в том, что в документации на LockFileEx такое четкое требование отсутствует, а ошибка проявляется очень редко вероятностно, вместо детерминированной ошибки. Более того, такое впечатление, что у всех кроме меня работает (да и у меня случайным образом оно работает). Рабочий проект тоже работал до поры до времени
Re[3]: Почему .Net-версия жестко виснет, а C++ - нет?
От: Igorxz  
Дата: 17.05.15 18:08
Оценка:
Здравствуйте, 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++ - нет?
От: Fortnum  
Дата: 17.05.15 18:13
Оценка:
Здравствуйте, 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 стабильно сразу встречается, на нулевом значении счетчика. Вообще, непонятно, от чего это зависит. От таймингов каких-то что ли?..
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.