Delphi: TThreadList иногда блокирует программу

Иногда эта функция блокирует мою программу, и она зависает, пока я ее не закрою. Что здесь не так?

function del_from_list(id:string):boolean;
var i : integer;
begin
  Result := True;
  try
    with global_list.LockList do
    begin
      for i:=0 to Count-1 do
      begin
        if Tthread_list(Items[i]).id = id then
        begin
          Delete(i);
          break;
        end;
      end;
    end;
  finally
    global_list.UnlockList;
  end;
end;

класс

  Tthread_list = class
  public
    id   : string;
    constructor Create(const id: string);
  end;

Я добавляю в список так:

global_list.Add(Tthread_list.Create('xxx'));

глобальный список - это глобальная переменная

var global_list : TThreadList = nil;

person waza123    schedule 17.12.2011    source источник
comment
Я не вижу причин, по которым это должно вызывать зависание. Это весь код в приложении, которое зависает?   -  person David Heffernan    schedule 17.12.2011
comment
Скорее всего, у вас есть классическая ситуация взаимоблокировки, но один этот код не может ее вызвать. Где-то должен быть еще один замок.   -  person David Heffernan    schedule 17.12.2011
comment
Вы удаляете элемент объекта класса в списке без предварительного освобождения объекта. Если из-за этого возникнет исключение, ваша программа заблокируется. Ответ Реми (который вы приняли) решает блокировку, но не основную ошибку.   -  person LU RD    schedule 17.12.2011
comment
@LURD, не могли бы вы объяснить, как ответ Реми решает проблему. Я просто должен что-то упустить.   -  person David Heffernan    schedule 17.12.2011
comment
@DavidHeffernan, я предположил, что это решило проблему, потому что это принятый ответ.   -  person LU RD    schedule 17.12.2011
comment
Я не понимаю, как list состоит только из конструктора строкового поля - даже не из базового класса? Если вы не поместите только один небольшой фрагмент этого класса?   -  person Jerry Dodge    schedule 17.12.2011
comment
@ waza123 вам, вероятно, следует изменить имя Tthread_list на что-то вроде TListItem, потому что вы можете запутать людей этим именем. В любом случае, я думаю, что у отрицательных голосов должна быть причина. Кроме плохого нейминга, я не понимаю, что людям не нравится в вашем вопросе.   -  person Wodzu    schedule 06.11.2012


Ответы (2)


Вам нужно вызвать LockList() вне блока try, а не внутри него, например:

function del_from_list(const id: string): boolean;
var
  List: TList;
  i : integer;
begin
  Result := False;
  List := global_list.LockList;
  try
    with List do
    begin
      for i :=0 to Count-1 do
      begin
        if Tthread_list(Items[i]).id = id then
        begin
          Delete(i);
          Result := True;
          break;
        end;
      end;
    end;
  finally
    global_list.UnlockList;
  end;
end;
person Remy Lebeau    schedule 17.12.2011
comment
Реми, я согласен с тем, что получение ресурсов (блокировка) должно выполняться до входа в блок try..finally, но выполнение этого другим способом (OP) должно вызывать проблемы только в том случае, если получение ресурсов завершается сбоем и выдает ошибку. Освежите мою память - может ли TThreadList.LockList когда-либо генерировать исключение? - person dthorpe; 17.12.2011
comment
@Remy Вы верите, что это изменение, которое действительно правильно, будет разницей между работающим приложением и тупиком? Если да, то можете ли вы объяснить, почему вы так считаете? Если нет, то почему вы опубликовали это как ответ? Кажется, это должен быть комментарий ко мне. - person David Heffernan; 17.12.2011
comment
@dthorpe LockList сегодня TMonitor.Enter(FLock); Result := FList;, поэтому он не будет выдаваться, если не будет повреждения кучи или ошибки в TMonitor. - person David Heffernan; 17.12.2011
comment
Из документации TList.Delete: Note: Delete does not free any memory associated with the item. Я думаю, что основной ошибкой здесь является отсутствие Tthread_list(Items[i]).Free. - person LU RD; 17.12.2011
comment
@LU RD Это просто утечка памяти. Это не приведет к тупику. - person David Heffernan; 17.12.2011
comment
@DavidHeffernan, я все еще озадачен тем, как ответ Реми может решить тупиковую ситуацию. Да, причиной может быть TMonitor, но, просматривая исходный код TMonitor в этом примере, я не могу найти слабых мест. - person LU RD; 17.12.2011
comment
@dthorpe: я не заметил, что TThreadList был обновлен для использования TMonitor. Вместо этого использовалось TCriticalSection. Это беспокоит меня, учитывая, что у TMonitor были известные проблемы, о которых несколько раз писали в блогах. - person Remy Lebeau; 17.12.2011
comment
@Remy У вас есть какое-нибудь объяснение, почему ваш ответ исправит тупик? - person David Heffernan; 17.12.2011
comment
Что касается перехода на TMonitor, это потому, что критические разделы - только для Windows, а TThreadList - это x-plat? - person David Heffernan; 17.12.2011

for цикл считает в неправильном направлении. При удалении участников вы ДОЛЖНЫ вести обратный отсчет, а не восходящий.

person Dennis Galvin    schedule 17.12.2011
comment
Не тогда, когда Break используется после вызова Delete(). Вам нужно только вернуться назад, если вы удаляете несколько элементов из списка. - person Remy Lebeau; 17.12.2011
comment
да, поэтому и перерыв. - person waza123; 17.12.2011