2015-06-28 1 views
0

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

Теперь я хочу открыть диалог настроек с данными конфигурации, чтобы я мог изменять данные внутри такого объекта. Но тогда я должен получить доступ к объектам в списке, пока поток также постоянно обращается к ним. Я знаю, как использовать критический раздел, но если поток входит в критический раздел каждый раз, когда он проверяет объект, тогда критический раздел будет введен и оставлен 1000 раз в секунду. Может быть, есть более умный способ?

Как сделать поточно с наименьшими накладными расходами, когда:

а) загрузки данных конфигурации в диалоговом окне Параметры формы (которая использует TListView в виртуальном режиме и требует доступа к списку объектов по требованию)

b) и сохранение формы ввода обратно к объекту?

EDIT: запрошено больше деталей.

объекты находятся в TList и в основном выглядеть следующим образом:

TConfigData = class 
    ID:Integer; 
    Name: String; 
    SwitchTime: TDateTime; 
end; 

Данные объекта ConfigData должен быть загружен в виде Settings Dialog, поэтому он может быть отредактирован, а затем, если пользователь щелкните OK, объект ConfigData должен быть обновлен, и поток будет с радостью использовать эти новые данные в следующий раз, когда будет получен доступ к обкекту. Однако обновление не должно происходить одновременно с тем, как поток читает объект ConfigData.

EDIT 2: Дополнительные детали:

Нити чтение ID, имя и SwitchTime, но только SwitchTime изменяется нитями. (Когда работа завершена, вычисляется новое время, и это то, что запускает следующее рабочее событие).

Диалог настроек может изменять как имя, так и время переключения, но не идентификатор.

+0

Вы можете установить логическое значение, указывающее, что необходимо проверить критический раздел. Чтение и запись логической переменной является атомарной. – Graymatter

+0

@Gray Это не помогает. Thread A читает false и продолжается. Прежде чем он сможет прочитать, поток B установил флаг true и начал изменять. Если вы действительно можете использовать такое логическое значение, почему бы блокировки существовать? –

+0

@Rimfire Звучит так, будто вам нужен лучший дизайн. Мы могли только догадываться, что это должно быть, поскольку вы не предоставили подробностей. Очень сложно советовать о том, как работать с данными, состав которых мы ничего не знаем. –

ответ

3

После немного мысли, вы можете уйти без использования критических секций вообще только с помощью InterlockedExchangePointer:

Вам нужно добавить процедуру для обновления конфигурации для элемента:

procedure TMyThread.UpdateConfig(const aIndex: Integer; const aID:Integer; 
    const aName: String; const aSwitchTime: TDateTime); 
var 
    newConfigToEdit: TConfigData; 
    oldValue: TConfigData; 
begin 
    newConfigToEdit := TConfigData.Create; 
    newConfigToEdit.ID := aID; 
    newConfigToEdit.Name := aName; 
    newConfigToEdit.SwitchTime := aSwitchTime; 
    repeat 
    oldvalue := InterlockedExchangePointer(FConfigDataList.List[aIndex], newConfigToEdit); 
    until (oldvalue <> nil) and (oldValue <> newConfigToEdit); 
    oldvalue.Free; // Free the replaced one 
end; 

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

procedure TMyThread.Execute; 
var 
    configToUse: TConfigData; 
begin 
    repeat 
    // Get the config and replace it with nil so it won't be changed 
    configToUse := InterlockedExchangePointer(FConfigDataList.List[idx], nil); 
    // Do stuff with the config 

    // Put the config back 
    FConfigDataList.List[idx] := configToUse; 
    // You could also use the line below instead of the assignment 
    // InterlockedExchangePointer(FConfigDataList.List[idx], configToUse); 
    until Terminated; 
end; 

Если вы хотите, чтобы начать рабочий поток с конфиг, то вы должны сделать клон него, а затем передать в клоне работника, потому что он может быть изменен.

+0

Попробуй это завтра. – Rimfire

+1

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

+0

@RobKennedy ОП говорил, что это было тысячу раз в секунду :) - Это говорит о том, что это решение ограничивает критические разделы. ОП необходимо будет определить, стоит ли это делать. – Graymatter

-2

Основной поток (псевдо) код (ObjList является глобальной переменной):

if ConfigUpdated(i) then 
    ObjList[i].ConfigVersion := ObjList[i].ConfigVersion + 1; 

Другие нити (s) (псевдо) код (ObjConfVer является локальным для резьбы)

for i := 0 to ObjCount - 1 do 
    if ObjConfVer[i] < ObjList[i].ConfigVersion then 
    begin 
    // Here you have to take care that main thread will can not change config while you handling it 
    // Do something about new config 
    ObjConfVer[i] := ObjList[i].ConfigVersion; 
    // Exit from critical section 
    end; 

Если у вас есть n темы, которые работают с тем же ObjList, что позволяет каждой нить что-то делать с измененным конфигом независимо.

BTW, если вы используете FPC/Lazarus эту ссылку, может быть полезным: Parallel procedures

+1

Я не вижу никакой синхронизации здесь. Что нужно остановить, если ObjConfVer [i]

+0

@DavidHeffernan И что? Данные были изменены в любом месте, и они должны быть обработаны. Главное - предотвратить частичные изменения данных. Таким образом, есть: 1) изменить данные 2) включить флаг 3) видеть, что флаг был увеличен и остановить (уведомлять) основной поток 4) обрабатывать новые данные 5) высвободить основную нить – Abelisto

+1

Мне кажется, что без синхронизации у вас есть гонка –

 Смежные вопросы

  • Нет связанных вопросов^_^