OmniThreadLibrary - код: 1816. Недостаточно квоты для обработки этой команды
Обновление 1: я включил следы стека всех потоков, а не только основных потоков - я думал, что этого уже достаточно.
Обновление 2: я снова открыл этот вопрос, поскольку даже после применения изменений, показанных в моем собственном вопросе, я все еще получаю тот же самый отчет об ошибке сегодня...
Обновление 3: кажется, что ошибка произошла, когда поток завершается, и сообщение потока, которое отправляется, когда произошла ошибка, было COmniTaskMsg_Terminated
, Теперь это очень странно - я заменил почти все звонки на Task.Comm.Send()
в моей программе с потокобезопасной очередью, так что я вполне уверен, что количество сообщений потока проходят Task.Comm
это очень мало, гораздо меньше, чем это может заполнить очередь сообщений Windows...
Я использую OmniThreadLibrary-3.03a (только что обновленный до последней svn, я посмотрю...) с Delphi XE4, и получаю сообщение об ошибке от конечного пользователя, сообщение об ошибке и трассировку стека потока, у которого была эта проблема: включен в конце.
На самом деле я уменьшил вероятность этой ошибки, заменив вызовы на Task.Comm.Send()
с потокобезопасной очередью, которая используется фоновыми потоками для отправки журналов в основной поток. Теперь я не знаю, где искать, наиболее вероятное место здесь (но внутри этого блока кода нет Task.Comm.Send()
звонки...):
//at this point, we are already in a thread other than the main thread.
//I've two instances of the very same thread running when the program runs.
myTasks := Parallel.ParallelTask.NumTasks(aTaskCount);// aTaskCount = 5
myTasks.Execute(
//the following anonymous method will be executed in sub-threads and will have
//multiple instances determined by the aTaskCount param.
procedure (const aSubTask: IOmniTask)
begin
//note: this code block runs in multiple sub-threads in parallel.
//aUidList can have tens of thousands (or even more) of item.
while aUidList.Take(uid) and (not Self.task.CancellationToken.IsSignalled) do
begin
ThreadedDoSomething();
end;
end
);//END sub-thread
Трассировки стека:
process id : $2f9c
allocated memory : 181.30 MB
largest free block : 1019.73 MB
executable : MyProgram.exe
exec. date/time : 2014-08-02 21:12
version : 1.0.7.256
compiled with : Delphi XE4
madExcept version : 4.0.9
callstack crc : $8b5fc164, $b1225a03, $7caf0d48
exception number : 1
exception class : EOSError
exception message : System Error. Code: 1816. Not enough quota is available to process this command.
thread $1160 (TOmniThread): <priority:-15>
0045c3de MyProgram.exe System.SysUtils RaiseLastOSError
0045c35b MyProgram.exe System.SysUtils RaiseLastOSError
0045c40f MyProgram.exe System.SysUtils Win32Check
0075b387 MyProgram.exe OtlContainerObserver 252 TOmniContainerWindowsMessageObserverImpl.Send
0076debb MyProgram.exe OtlTaskControl 1378 TOmniTask.InternalExecute
0076db6a MyProgram.exe OtlTaskControl 1274 TOmniTask.Execute
0077423c MyProgram.exe OtlTaskControl 3091 TOmniThread.Execute
004ab243 MyProgram.exe madExcept HookedTThreadExecute
0053c596 MyProgram.exe System.Classes ThreadProc
0040a5b4 MyProgram.exe System 150 ThreadWrapper
004ab129 MyProgram.exe madExcept CallThreadProcSafe
004ab18e MyProgram.exe madExcept ThreadExceptFrame
75223368 kernel32.dll BaseThreadInitThunk
>> created by main thread ($1408) at:
007741a1 MyProgram.exe OtlTaskControl 3080 TOmniThread.Create
main thread ($1408):
0090f52d MyProgram.exe VirtualTrees 33601 TBaseVirtualTree.SortTree
00906fd1 MyProgram.exe VirtualTrees 28348 TBaseVirtualTree.EndUpdate
008f3563 MyProgram.exe VirtualTrees 17031 TBaseVirtualTree.SetRootNodeCount
00b66d99 MyProgram.exe BackupControlFrame 219 TfraBackupControl.AddLog
00b66be2 MyProgram.exe BackupControlFrame 179 TfraBackupControl.AddLog
00b69595 MyProgram.exe BackupControlFrame 844 TfraBackupControl.TimerReadLogTimer
005f430b MyProgram.exe Vcl.ExtCtrls TTimer.Timer
005f41ef MyProgram.exe Vcl.ExtCtrls TTimer.WndProc
0053fca4 MyProgram.exe System.Classes StdWndProc
76777885 USER32.dll DispatchMessageW
00656b87 MyProgram.exe Vcl.Forms TApplication.ProcessMessage
00656bca MyProgram.exe Vcl.Forms TApplication.HandleMessage
00656f05 MyProgram.exe Vcl.Forms TApplication.Run
00b83f34 MyProgram.exe MyProgram 130 initialization
75223368 kernel32.dll BaseThreadInitThunk
thread $22a4:
771d0156 ntdll.dll NtWaitForMultipleObjects
75223368 kernel32.dll BaseThreadInitThunk
thread $1604:
771d0156 ntdll.dll NtWaitForMultipleObjects
761215e3 KERNELBASE.dll WaitForMultipleObjectsEx
752219f7 kernel32.dll WaitForMultipleObjectsEx
76780864 USER32.dll MsgWaitForMultipleObjectsEx
76780b64 USER32.dll MsgWaitForMultipleObjects
004ab129 MyProgram.exe madExcept CallThreadProcSafe
004ab18e MyProgram.exe madExcept ThreadExceptFrame
75223368 kernel32.dll BaseThreadInitThunk
>> created by main thread ($1408) at:
738778e1 gdiplus.dll
thread $3060 (TWorkerThread):
771cf8ca ntdll.dll NtWaitForSingleObject
76121497 KERNELBASE.dll WaitForSingleObjectEx
7522118f kernel32.dll WaitForSingleObjectEx
75221143 kernel32.dll WaitForSingleObject
008e2dbe MyProgram.exe VirtualTrees 6364 TWorkerThread.Execute
004ab243 MyProgram.exe madExcept HookedTThreadExecute
0053c596 MyProgram.exe System.Classes ThreadProc
0040a5b4 MyProgram.exe System 150 ThreadWrapper
004ab129 MyProgram.exe madExcept CallThreadProcSafe
004ab18e MyProgram.exe madExcept ThreadExceptFrame
75223368 kernel32.dll BaseThreadInitThunk
>> created by main thread ($1408) at:
008e2cda MyProgram.exe VirtualTrees 6312 TWorkerThread.Create
thread $22c0:
771d1f3f ntdll.dll NtWaitForWorkViaWorkerFactory
75223368 kernel32.dll BaseThreadInitThunk
thread $33d0:
771cf8ca ntdll.dll NtWaitForSingleObject
76121497 KERNELBASE.dll WaitForSingleObjectEx
7522118f kernel32.dll WaitForSingleObjectEx
004ab129 MyProgram.exe madExcept CallThreadProcSafe
004ab18e MyProgram.exe madExcept ThreadExceptFrame
75223368 kernel32.dll BaseThreadInitThunk
>> created by thread $31fc at:
7327325b rasman.dll
thread $2dc4:
771cf8ca ntdll.dll NtWaitForSingleObject
764b2f7b WS2_32.dll WahReferenceContextByHandle
764b6a25 WS2_32.dll select
004ab129 MyProgram.exe madExcept CallThreadProcSafe
004ab18e MyProgram.exe madExcept ThreadExceptFrame
75223368 kernel32.dll BaseThreadInitThunk
>> created by main thread ($1408) at:
75349791 WININET.dll
thread $2148:
771d1f3f ntdll.dll NtWaitForWorkViaWorkerFactory
75223368 kernel32.dll BaseThreadInitThunk
thread $2258 (TOmniThread): <priority:-15>
771d0156 ntdll.dll NtWaitForMultipleObjects
761215e3 KERNELBASE.dll WaitForMultipleObjectsEx
752219f7 kernel32.dll WaitForMultipleObjectsEx
76780864 USER32.dll MsgWaitForMultipleObjectsEx
00771942 MyProgram.exe OtlTaskControl 2379 TOmniTaskExecutor.WaitForEvent
00770ddc MyProgram.exe OtlTaskControl 2148 TOmniTaskExecutor.MainMessageLoop
0076fb2e MyProgram.exe OtlTaskControl 1849 TOmniTaskExecutor.DispatchMessages
0076e7d9 MyProgram.exe OtlTaskControl 1636 TOmniTaskExecutor.Asy_Execute
0076dd59 MyProgram.exe OtlTaskControl 1354 TOmniTask.InternalExecute
0076db6a MyProgram.exe OtlTaskControl 1274 TOmniTask.Execute
0077423c MyProgram.exe OtlTaskControl 3091 TOmniThread.Execute
0040a5b4 MyProgram.exe System 150 ThreadWrapper
004ab129 MyProgram.exe madExcept CallThreadProcSafe
004ab18e MyProgram.exe madExcept ThreadExceptFrame
75223368 kernel32.dll BaseThreadInitThunk
>> created by thread $1160 (TOmniThread) at:
007741a1 MyProgram.exe OtlTaskControl 3080 TOmniThread.Create
thread $618 (TOTPWorkerThread): <priority:-15>
771d0156 ntdll.dll NtWaitForMultipleObjects
761215e3 KERNELBASE.dll WaitForMultipleObjectsEx
752219f7 kernel32.dll WaitForMultipleObjectsEx
752241d3 kernel32.dll WaitForMultipleObjects
0074b749 MyProgram.exe DSiWin32 1949 DSiWaitForTwoObjects
007782ca MyProgram.exe OtlComm 478 TOmniCommunicationEndpoint.ReceiveWait
007639cc MyProgram.exe OtlThreadPool 625 TOTPWorkerThread.Execute
004ab243 MyProgram.exe madExcept HookedTThreadExecute
0053c596 MyProgram.exe System.Classes ThreadProc
0040a5b4 MyProgram.exe System 150 ThreadWrapper
004ab129 MyProgram.exe madExcept CallThreadProcSafe
004ab18e MyProgram.exe madExcept ThreadExceptFrame
75223368 kernel32.dll BaseThreadInitThunk
>> created by thread $2258 (TOmniThread) at:
0076348c MyProgram.exe OtlThreadPool 537 TOTPWorkerThread.Create
thread $5f0 (TOTPWorkerThread): <priority:-15>
771d0156 ntdll.dll NtWaitForMultipleObjects
761215e3 KERNELBASE.dll WaitForMultipleObjectsEx
752219f7 kernel32.dll WaitForMultipleObjectsEx
752241d3 kernel32.dll WaitForMultipleObjects
0074b749 MyProgram.exe DSiWin32 1949 DSiWaitForTwoObjects
007782ca MyProgram.exe OtlComm 478 TOmniCommunicationEndpoint.ReceiveWait
007639cc MyProgram.exe OtlThreadPool 625 TOTPWorkerThread.Execute
004ab243 MyProgram.exe madExcept HookedTThreadExecute
0053c596 MyProgram.exe System.Classes ThreadProc
0040a5b4 MyProgram.exe System 150 ThreadWrapper
004ab129 MyProgram.exe madExcept CallThreadProcSafe
004ab18e MyProgram.exe madExcept ThreadExceptFrame
75223368 kernel32.dll BaseThreadInitThunk
>> created by thread $2258 (TOmniThread) at:
0076348c MyProgram.exe OtlThreadPool 537 TOTPWorkerThread.Create
thread $2a84 (TOTPWorkerThread): <priority:-15>
771d0156 ntdll.dll NtWaitForMultipleObjects
761215e3 KERNELBASE.dll WaitForMultipleObjectsEx
752219f7 kernel32.dll WaitForMultipleObjectsEx
752241d3 kernel32.dll WaitForMultipleObjects
0074b749 MyProgram.exe DSiWin32 1949 DSiWaitForTwoObjects
007782ca MyProgram.exe OtlComm 478 TOmniCommunicationEndpoint.ReceiveWait
007639cc MyProgram.exe OtlThreadPool 625 TOTPWorkerThread.Execute
004ab243 MyProgram.exe madExcept HookedTThreadExecute
0053c596 MyProgram.exe System.Classes ThreadProc
0040a5b4 MyProgram.exe System 150 ThreadWrapper
004ab129 MyProgram.exe madExcept CallThreadProcSafe
004ab18e MyProgram.exe madExcept ThreadExceptFrame
75223368 kernel32.dll BaseThreadInitThunk
>> created by thread $2258 (TOmniThread) at:
0076348c MyProgram.exe OtlThreadPool 537 TOTPWorkerThread.Create
thread $1ca0 (TOTPWorkerThread): <priority:-15>
771d0156 ntdll.dll NtWaitForMultipleObjects
761215e3 KERNELBASE.dll WaitForMultipleObjectsEx
752219f7 kernel32.dll WaitForMultipleObjectsEx
752241d3 kernel32.dll WaitForMultipleObjects
0074b749 MyProgram.exe DSiWin32 1949 DSiWaitForTwoObjects
007782ca MyProgram.exe OtlComm 478 TOmniCommunicationEndpoint.ReceiveWait
007639cc MyProgram.exe OtlThreadPool 625 TOTPWorkerThread.Execute
004ab243 MyProgram.exe madExcept HookedTThreadExecute
0053c596 MyProgram.exe System.Classes ThreadProc
0040a5b4 MyProgram.exe System 150 ThreadWrapper
004ab129 MyProgram.exe madExcept CallThreadProcSafe
004ab18e MyProgram.exe madExcept ThreadExceptFrame
75223368 kernel32.dll BaseThreadInitThunk
>> created by thread $2258 (TOmniThread) at:
0076348c MyProgram.exe OtlThreadPool 537 TOTPWorkerThread.Create
thread $3248 (TOTPWorkerThread): <priority:-15>
771d0156 ntdll.dll NtWaitForMultipleObjects
761215e3 KERNELBASE.dll WaitForMultipleObjectsEx
752219f7 kernel32.dll WaitForMultipleObjectsEx
752241d3 kernel32.dll WaitForMultipleObjects
0074b749 MyProgram.exe DSiWin32 1949 DSiWaitForTwoObjects
007782ca MyProgram.exe OtlComm 478 TOmniCommunicationEndpoint.ReceiveWait
007639cc MyProgram.exe OtlThreadPool 625 TOTPWorkerThread.Execute
004ab243 MyProgram.exe madExcept HookedTThreadExecute
0053c596 MyProgram.exe System.Classes ThreadProc
0040a5b4 MyProgram.exe System 150 ThreadWrapper
004ab129 MyProgram.exe madExcept CallThreadProcSafe
004ab18e MyProgram.exe madExcept ThreadExceptFrame
75223368 kernel32.dll BaseThreadInitThunk
>> created by thread $2258 (TOmniThread) at:
0076348c MyProgram.exe OtlThreadPool 537 TOTPWorkerThread.Create
thread $11c8:
771d1f3f ntdll.dll NtWaitForWorkViaWorkerFactory
75223368 kernel32.dll BaseThreadInitThunk
Со ссылкой на этот ответ на другой вопрос, кажется, что очередь сообщений Windows, используемая OTL как канал связи, заполнена.
4 ответа
Исключение возникает в OTL здесь после попытки вызова PostMessage
,
procedure TOmniContainerWindowsMessageObserverImpl.Send(aMessage: cardinal;
wParam, lParam: integer);
begin
Win32Check(PostMessage(cwmoHandle, aMessage, wParam, lParam));
end;
Исключение указывает, что очередь сообщений для окна с дескриптором cwmoHandle
заполнен и не обслуживается своевременно. Дескриптор этого окна создан TOmniMessageQueue
когда вы назначаете его OnMessage
имущество. Скорее всего, поток, который выполняет это назначение, блокируется (или блокируется слишком долго) и не обрабатывает сообщения своевременно.
procedure TOmniMessageQueue.SetOnMessage(const value: TOmniMessageQueueMessageEvent);
begin
if (not assigned(mqWinMsgObserver.OnMessage)) and assigned(value) then begin // set up observer
mqWinMsgObserver.Window := DSiAllocateHWnd(WndProc); // CREATED HERE**
mqWinMsgObserver.Observer := CreateContainerWindowsMessageObserver(
mqWinMsgObserver.Window, MSG_CLIENT_MESSAGE, 0, 0);
ContainerSubject.Attach(mqWinMsgObserver.Observer, coiNotifyOnAllInserts);
mqWinMsgObserver.Observer.Activate;
end
else if assigned(mqWinMsgObserver.OnMessage) and (not assigned(value)) then begin // tear down observer
mqWinMsgObserver.Observer.Deactivate;
ContainerSubject.Detach(mqWinMsgObserver.Observer, coiNotifyOnAllInserts);
FreeAndNil(mqWinMsgObserver.Observer);
DSiDeallocateHWnd(mqWinMsgObserver.Window);
end;
mqWinMsgObserver.OnMessage := value;
end;
Чтобы ответить более конкретно, нам нужно увидеть больше кода, показывающего, где и как вы реализуете свои объекты OTL. Единственный другой ключ - ваш пост.
thread $1160 (TOmniThread): <priority:-15>
Это показывает, что поток, вызвавший исключение, работает с более низким уровнем приоритета. Само по себе это не объясняет ничего, кроме того, что ваше приложение активно меняет уровни приоритетов потоков. Без кода для анализа невозможно сказать, но структура вашей программы может вызывать проблемы с приоритетом инверсии, которые расстраивают способность собственного потока обрабатывать сообщения.
Вот мое решение - для тех, кто встречался с такой же проблемой. Но я принял @J.. это ответ, потому что он помог мне найти это решение.
var
waitEvent : IOmniCancellationToken;
begin
// create the 'end of all sub-threads' signal
waitEvent := CreateOmniCancellationToken;
myTasks.NoWait.Execute(//Note the NoWait call
procedure (const aSubTask: IOmniTask)
begin
doSomeDownloadTasks()
end
).OnStop(procedure
begin
waitEvent.Signal;//once all subthreads are stopped, signal the parent thread.
end
);
// wait for the sub-threads to stop, and while waiting, clear the Windows messages periodically
while not waitEvent.IsSignalled do
begin
doSomeDataSavingTasks();
//self.Task has a hidden Window handle, here we constantly empty its message queue
//, to aovid the 'Not enough quota is available to process this command' error.
Task.Comm.Receive(myMsg);
end;
У меня также была эта проблема, и в моем случае я забыл удалить некоторые task.comm.send()
после того, как я удалил интерфейс IOmniTaskConfig из этапов конвейера, который для отладки.
С другой стороны, вы можете позвонить MsgData._ReleaseAndClear;
освободить сообщение, как это сделано в TOmniCommunicationEndpoint.SendWait()
в случае ошибок.
procedure TPmOTLTaskConfigLogger.OnMessage(const ATask: IOmniTaskControl;
const AMsg: TOmniMessage);
begin
try
//your code
finally
AMsg.MsgData._ReleaseAndClear;
end;
end;
Следующий SSCCE вызывает исключение "Превышена квота" (а иногда и некоторые AV и исключение "Очередь заполнена") с использованием конвейера. Превышенная цитата вызвана otSharedInfo_ref.Monitor.Send()
в TOmniTask.InternalExecute()
,
program sscce_otl_quota_exceeded_2;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
OtlCommon,
OtlCollections,
OtlTask,
OtlParallel;
procedure Stage1(const input, output: IOmniBlockingCollection; const task: IOmniTask);
var
LInputValue: TOmniValue;
begin
for LInputValue in input do
task.Comm.Send(1, LInputValue.AsInteger);//fill the queue
end;
var
FPipeline: IOmniPipeline;
LOutputValue: TOmniValue;
i: Integer;
begin
try
FPipeline := Parallel.Pipeline
//using multiple thread provokes the "Quota exceeded" exception after some
//"Queue is full" exceptions and AVs. The "Quota exceeded" is triggered by
//otSharedInfo_ref.Monitor.Send() in TOmniTask.InternalExecute().
//Note: Sometimes the debugger hangs (XE3)
.Stage(Stage1).NumTasks(16)
.Run;
for i := 1 to 9999 do
FPipeline.Input.Add(i);
FPipeline.Input.CompleteAdding;
while not FPipeline.WaitFor(10) do
Sleep(1);
FPipeline := nil;
Writeln('End');
ReadLn;
except
on E: Exception do begin
Writeln(E.ClassName, ': ', E.Message);
ReadLn;
end;
end;
end.
Мы также столкнулись с исключениями "Превышена квота" и "Очередь заполнена". Мы решили эту проблему, создав наш собственный монитор и вызвав на нем сообщения о процессах.
мы создавали нашу собственную задачу и держали ссылку на контроллер задач. При этом мы будем использовать каналы связи напрямую для отправки и получения сообщений.
//Setup
FTaskCtrl := CreateTask(taskObj, name)
.SetTimer(TASK_MSG_PUMP_TIMER_ID, TASK_MSG_PUMP_INTERVAL, DEBUG_MSG_PUMP)
.OnTaskTerminated(OnTaskTerminated)
.OnTaskMessage(OnTaskMessage);
//Sending messages to the task
FTaskCtrl.Comm.Send(CONST_MSG_NUM, TOmniValue.CastFrom<TOurMsgObj>(msgObjToSend));
//Getting messages from the task, done once every 100ms
FTaskCtrl.Comm.Receive(msgObj);
Обратите внимание, что сообщение таймера является сообщением о том, что рабочая задача еще жива.
В основном цикле нашего потока приложения или потока бизнес-движка мы "качали" комм, чтобы убедиться, что все сообщения были обработаны. Вызов насоса будет вызываться примерно каждые 100 мс.
procedure MainClass.Pump;
var
msg : TOmniMessage;
begin
if FTaskCtrl.Comm.Receive(msg) then
OnTaskMessage(FTaskCtrl,msg);
end;
Мы обнаружили, что это оставляет слишком много сообщений в очереди, поэтому мы изменили это на.
procedure MainClass.Pump;
var
msg : TOmniMessage;
begin
while FTaskCtrl.Comm.Receive(msg) do
OnTaskMessage(FTaskCtrl,msg);
end;
Это приведет к взрывному поведению. Также мы все равно получим ошибки по завершаемой задаче. Конкретное сообщение завершения, отправленное задачей при закрытии, не было передано.
Мы обнаружили, что, хотя мы не создавали монитор, он создавался внутри. Также его wndproc не вызывался, или, по крайней мере, ему давалось время для обработки отправленного ему сообщения.
Поэтому, в конце концов, мы вернулись к более простой реализации, которую мы видели в ряде примеров для потока Omni.
//Setup
FMonitor := TOmniEventMonitor.Create(nil);
FMonitor.OnTaskTerminated := Self.OnTaskTerminated;
FMonitor.OnTaskMessage := Self.OnTaskMessage;
FTaskCtrl := CreateTask(taskObj, name).SetTimer(DEBUG_TASK_MSG_PUMP_TIMER_ID, DEBUG_TASK_MSG_PUMP_INTERVAL, DEBUG_MSG_PUMP);
FTaskCtrl := FMonitor.Monitor(FTaskCtrl);
//Sending messages to the task
FTaskCtrl.Comm.Send(CONST_MSG_NUM, TOmniValue.CastFrom<TOurMsgObj>(msgObjToSend));
//Once per internal loop of the application/main logic thread. Once every 50ms.
FMonitor.ProcessMessages;
Выполнение вышеизложенного означало, что мониторы были вызваны wndproc и вся его логика была выполнена. Мы видели, например, что сообщения о завершении теперь правильно обрабатываются.
Спасибо J... за первоначальный пост, который подсказал нам в правильном направлении.