Является ли TCustomClientDataSet CloneCursor потокобезопасным?

Я читал, что клонированные наборы TClientDataSet являются потокобезопасными, если клоны доступны только для чтения (без публикации записей или перезагрузки данных).

Delphi - Является ли TClientDataset потокобезопасным?

Но меня беспокоит сам метод CloneCursor; метод заканчивается вызовом метода SetNotifyCallback исходного набора данных, который передает метод обратного вызова своему IDSCursor, если FNotifyCallback имеет значение False:

procedure TCustomClientDataSet.SetNotifyCallback;
begin
  if not FNotifyCallback then
  begin
    Check(FDSCursor.SetNotifyCallBack(IntPtr(Self), @TCustomClientDataSet.NotifyCallback));
    FNotifyCallback := True;
  end;
end; 

В маловероятном случае двух наборов данных, A и B, в отдельных потоках, клонирующих набор данных C почти в одно и то же время (FNotifyCallback набора данных C False), при этом A немного опережает B. B начинает выполнение SetNotifyCallBack C после того, как A проверил FNotifyCallback, но до А установил для FNotifyCallBack значение True в методе, показанном выше.

В этом сценарии метод FDSCursor SetNotifyCallback набора данных C вызывается почти одновременно двумя разными потоками; метод, который записывает ссылку на переменную внутри IDSCursor (я полагаю, не удалось найти исходный код). По общему признанию, оба вызова запрашивают сохранение одной и той же ссылки, но, как следует из заголовка, является ли CloneCursor потокобезопасным?

Пожалуйста, примите мою благодарность заранее.


person CAnder    schedule 27.08.2015    source источник
comment
Это хороший вопрос, если бы вы только изменили свой последний абзац всего на один вопрос. Действительно ли CloneCursor является потокобезопасным или нет?   -  person Jerry Dodge    schedule 28.08.2015
comment
Если вы пытаетесь клонировать курсор из одного набора данных в 2 потока, то у вас есть 2 потока, обращающихся к одному и тому же набору данных, который, как правило, не является потокобезопасным. То же самое относится к клонированию курсора из нескольких наборов данных в один. В обоих случаях вы имеете дело не с клонированным набором данных TClientDataset, а с общим набором данных TClientDataset между двумя потоками. Обычно с такими вещами вы хотите клонировать элемент, а затем передать его в поток.   -  person Graymatter    schedule 28.08.2015
comment
@Jerry: Это очень четко задано в заголовке вопроса, и последний абзац также кажется довольно ясным.   -  person Ken White    schedule 28.08.2015
comment
@Jerry: Подумав, заключительные вопросы не нужны, и было бы лучше оставить «является ли CloneCursor потокобезопасным?». Я отредактирую.   -  person CAnder    schedule 28.08.2015


Ответы (2)


Проблема, представленная в вопросе, связана с, хотя и маловероятным событием, когда два потока, A и B, одновременно клонируют C, и это представляет собой первые вызовы метода C CloneCursor с момента открытия C (FNotifyCallback False). Это приводит к возможности одновременного вызова IDSCursor SetNotifyCallback двумя отдельными потоками.

Основываясь на комментариях Graymatter, одно из решений состоит в том, чтобы гарантировать, что первый вызов C CloneCursor происходит внутри потока C. Как только это сделано, FNotifyCallback принимает значение True и остается таковым до тех пор, пока C остается открытым.

С FNotifyCallback True код для SetNotifyCallback C разрешается в следующие инструкции (на моем ПК):

006418CB  cmp byte ptr [ebx+$00000290],$00
006418D2  jnz $006418fa
{code to call IDSCursor SetNotifycallback}
006418FA 5B               pop ebx
006418FB C3               ret 

С FNotifyCallback True SetNotifyCallback сводится к сравнению содержимого FNotifyCallback с нулевым значением перед переходом к инструкциям pop и ret.

Рискуя быть избитым, я считаю, что если FNotifyCallback имеет значение True, то последующие вызовы SetNotifyCallback являются потокобезопасными.

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

На моем ПК это разрешается одной инструкцией по увеличению ячейки памяти:

inc dword ptr [eax+$04] 

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

Таким образом, я считаю, что CloneCursor является потокобезопасным, если FNotifyCallback имеет значение True.

person CAnder    schedule 30.08.2015
comment
Это может быть так прямо сейчас. Я недостаточно подробно изучил ваш ответ. Проблема в том, что вы рискуете, идя вперед. Если специально не указано, что объект является потокобезопасным, вам придется проверять каждый выпуск Delphi, чтобы убедиться, что он по-прежнему является потокобезопасным. Единственный способ обойти это — не делиться объектами между потоками. - person Graymatter; 30.08.2015

Довольно старый вопрос, но у меня нигде нет хорошего ответа, и мне пришлось немного покопаться. Поэтому пишите сюда для тех, кто все еще может работать с такими вещами.

CloneCursor не является потокобезопасным, по крайней мере, в Delphi 2007, на котором я недавно тестировал это. Если вы вызываете CloneCursor более чем в одном потоке, где каждый поток имеет свой собственный клонированный набор данных, причем все они клонируются из одного и того же исходного набора данных, вы получите AV или другие ошибки. Это легко воспроизвести: просто создайте два или более потока, которые продолжают клонировать один и тот же набор данных в цикле while, и вы получите ошибки максимум через несколько секунд. Но можно просто защитить вызов CloneCursor критической секцией и работать со своим клоном вне критической секции.

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

person Thiago Linhares de Oliveira    schedule 08.01.2019