2010-02-21 4 views
7

백그라운드 스레드에서 데이터베이스 쿼리를 실행하려고합니다. OmniThread 라이브러리는 모든 스레드에 도움이 될 것입니다. 그러나 지금까지 이해할 수없는 것이 있습니다.백그라운드 스레드에서 생성 된 쿼리 결과에 올바르게 액세스하는 방법은 무엇입니까?

모든 스레드는 별도의 데이터베이스 연결이 필요합니다. 따라서 백그라운드 스레드는 DB 연결을 생성하고 쿼리를 생성 한 다음 실행합니다.

이제 백그라운드 스레드의 쿼리 개체를 사용하여 쿼리 결과에 액세스 할 수 있습니다.
그러나 쿼리가 실행 된 후 메인 스레드의 쿼리 결과에 액세스하려고합니다.

백그라운드 스레드 쿼리 개체를 참조하는 경우 다른 스레드에서 DB 연결에 액세스하고 있기 때문에 문제가 발생합니까?

제가 알고 있듯이이 경우 주 스레드는 별도의 DB 연결이 필요하지 않으므로 좋지 않은 백그라운드 스레드의 스레드를 사용하십시오.

내 생각이 왜 왜곡되어 있으며이를 수행하는 올바른 방법은 무엇입니까?

답변

12

당신의 OTL 작업이 기준과 일치하는 회사의 정렬 된 목록로드 할 필요가있는 경우 :

// create and open query to fetch list of companies 
while not qryCompanies.Eof do begin 
    C := TCompany.Create; 
    try 
    C.LoadFromDataset(qryCompanies); 
    Companies.Add(C); 
    except 
    C.Free; 
    raise; 
    end; 
    qryCompanies.Next; 
end; 

C이 회사의 비즈니스 오브젝트입니다. 객체 (TCompany) 또는 객체가 구현 한 인터페이스 (ICompany)가 될 수 있습니다. CompaniesTList<TCompany> 또는 TList<ICompany>입니다. 작업의 끝에서 당신은 VCL 스레드에 기업의 목록을 보내 :

Task.Comm.Send(TOmniMessage.Create(MSGID_LIST_OF_COMPANIES, Companies)); 

를 양식에 당신이 당신의 작업을 모니터링하는 otlEventMonitor 인스턴스의 OnTaskMessage 이벤트를 처리 기업의 목록을 표시 할 위치 :

procedure TListBaseFrame.otlEventMonitorTaskMessage(
    const task: IOmniTaskControl); 
var 
    MsgID: word; 
    MsgValue: TOmniValue; 
begin 
    task.Comm.Receive(MsgID, MsgValue); 
    Assert(MsgValue.IsInterface); 
    if fLoaderTask = task then begin 
    SetLoadedData(MsgID, MsgValue.AsInterface); // or MsgValue.AsObject); 
    fLoaderTask := nil; 
    end; 
end; 

기업 목록이 이전 목록을 대체하며 그리드에 표시 될 수 있습니다.

마찬가지로 회사 개체/인터페이스 하나를 반환하고 표시하고 편집 할 수 있습니다. 생각의 가치가

두 가지 : 인터페이스

  • 지금까지 선호하는 경우 객체, 멀티 스레드 프로그램을 작성은을 재고 할 수있는 이유가 될 수 있습니다. 백그라운드 스레드에서 객체를 만든 다음 VCL 스레드에 전달하고 백그라운드 스레드에서 객체를 잊어 버리면 객체가 제대로 작동 할 수 있습니다. 그러나 응용 프로그램에서 개체를 캐싱하고 아직로드되지 않았거나 변경된 데이터베이스의 레코드 만로드하면 성능이 훨씬 향상 될 수 있음을 알았습니다.모든 테이블에는 변경 색인 (64 비트 정수, 시간 스탬프가 잘 작동 할 수 있음)이 첨부되어 있으며 모든 업데이트와 함께 변경됩니다. 대신

    select * from foo where (...) order by (...) 
    

    를 실행의 난 단지 이제까지

    select id, change_index from foo where (...) order by (...) 
    

    다음 캐시에 체크 실행 여부를 동일한 ID 그래서 캐시를 반환하는 경우 이미 존재 (기본 키) 및 변경 인덱스 오브젝트 새 비즈니스 오브젝트를 작성하지 않고 모든 컬럼을로드하는 경우에만.

    개체를 캐시하면 다중 스레드에서 개체를 참조하게되고 소유권 문제가 너무 복잡해 지므로 참조 카운트를 기반으로하는 평생 관리가 정상적으로 유지되는 유일한 방법입니다. 객체 대신 인터페이스를 사용하면이 점에서 많은 도움이됩니다.

  • 여러 스레드가 동시에 액세스 할 수있는 경우, 각 Business Object에 동기화 오브젝트를 추가해야합니다. 물론 가능하지만 추가적인 복잡성과 잠재적 교착 상태가 발생할 수 있습니다. 비즈니스 오브젝트를 변경 불가능한 것으로 구현하면 잠금이 필요하지 않습니다. 나는 그 접근 방식을 점점 더 많이 사용하고 있으며 익숙해지기까지는 많은 일을 단순화 할 수 있습니다.

+0

완벽한! 그게 정확히 제가 누락 된 것입니다 ... 결과를 다른 스레드로 가져 오는 방법. 대단히 감사합니다! – Holgerwa

+0

+1 훌륭한 답변! – jpfollenius

+0

평소와 같이 훌륭한 답변입니다! – gabr

5

아마도 GUI에서 db-aware 구성 요소를 사용하지 않는 것이 가장 좋습니다. 스레드는 데이터베이스와 통신하고 비즈니스 오브젝트에 정보를 저장해야하며,이를 주 스레드로 보내야합니다 (표시됩니다).

멀티 스레딩은 구현의 관점 에서뿐만 아니라 응용 프로그램 디자인의 관점에서도 어렵습니다. 잘 정의 된 입력과 출력을 가진 별도의 레이어로 배경 스레드를 생각하면 대개 가장 좋습니다.

+0

"비즈니스 개체에 정보를 저장합니다." 그게 내가 붙어있는 지점이야. 데이터 인식 컴포넌트를 사용하지 않고 쿼리 결과 데이터를 주 스레드로 가져와야합니다. 비즈니스 객체로 무엇을 참조합니까? – Holgerwa

+1

Business Object = 데이터베이스의 데이터를 포함하고 객체 지향 프로그래밍 방식으로 변환 된 (즉, SQL 결과처럼 보이지 않지만 문제를 연구 할 때 종이에 디자인 한 것과 비슷합니다) 클래스의 인스턴스입니다. – gabr

관련 문제