2012-05-11 2 views
3

나는 꽤 바닐라 웹 서비스 (구시대 asmx)를 가지고있다. 메소드 중 하나는 클라이언트에 반환 된 결과와 아무런 관련이없는 비동기 처리를 시작합니다. ThreadPool.QueueUserWorkItem의 DoAsyncStuffHere 방법을 만들어 사용C# 웹 서비스에서 프로듀서/소비자 대기열 구현

[System.Web.Services.WebMethod] 
public List<Foo> SampleWebMethod(string id) 
{ 
    // sample db query 
    var foo = db.Query<Foo>("WHERE [email protected]",id); 

    // kick of async stuff here - for example firing off emails 
    // dont wait to send result 
    DoAsyncStuffHere(); 

    return foo; 

} 

내 초기 구현 : 희망, 작은 조각은 아래의 의미가 있습니다. 그래서, 그것은 다음과 같이 보입니다 :

public void DoAsyncStuffHere() 
{ 
    ThreadPool.QueueUserWorkItem(delegate 
    { 
     // DO WORK HERE 
    }); 
} 

이 접근법은 저 부하 조건에서 잘 작동합니다. 그러나, 나는 꽤 높은 부하를 처리 할 수있는 무언가가 필요하다. 따라서 생산자/소비자 패턴이 가장 좋은 방법 인 것 같습니다. 내가 혼란 스러워요

모든 일이 모든 인스턴스 웹 서비스의에 걸쳐 단일 스레드 큐에 의해 수행되는 것을 제한하는 방법이다. 웹 서비스의 모든 인스턴스에서 액세스 할 단일 대기열을 설정하는 것이 가장 좋은 방법은 무엇입니까?

+0

"웹 서비스의 모든 인스턴스에서"라고 말하면, 여러 요청 처리 스레드가있는 프로세스 하나를 의미합니까, 아니면 부하 분산 설정과 같이 별도로 실행되는이 ASP.NET 프로젝트의 여러 복사본을 의미합니까? – mbeckish

+0

여러 요청 처리 스레드가있는 하나의 프로세스. – user163757

답변

4

당신은 기본 모음으로 System.Collections.Concurrent.ConcurrentQueue<T>으로 System.Collections.Concurrent.BlockingCollection<T>를 사용할 수 있습니다.
네임 스페이스의 이름에서 알 수 있듯이 컬렉션은 스레드로부터 안전합니다.

Take() 메서드를 사용하여 컬렉션에서 항목을 가져 오려면 소비자 스레드 (또는 일부)를 시작하십시오. 사용할 수있는 항목이 없으면 스레드가 차단됩니다.

DoAsyncStuffHere 메서드는 항목을 BlockingCollection에 추가합니다. 이러한 항목은 시작되지 않을 수 있습니다 System.Threading.Tasks.Task 개체; 이 경우 소비자 스레드는 컬렉션에서 가져온 작업을 Start으로 처리합니다.

0

대기열을 데이터베이스 테이블로 구현하는 것이 쉬운 방법 중 하나입니다.

생산자는 웹 서비스의 각 인스턴스에서 처리하는 요청 스레드가됩니다.

소비자는 대기열을 모니터링하고 항목을 한 번에 하나씩 처리하는 연속적으로 실행되는 모든 프로세스 (Windows Forms 응용 프로그램, Windows 서비스, 데이터베이스 작업 등) 일 수 있습니다.

+1

그래도 작동 할 것이지만이 접근법의 단점은 기존 서비스 내부에서 수행하는 것보다 유지 관리가 훨씬 많다는 것입니다. 유지 관리 할 새 데이터베이스/테이블. 개발, 품질 보증 및 배치를위한 또 다른 서비스; 서버에 대한 또 다른 프로세스가 걱정됩니다. – eouw0o83hf

+0

예.하지만 모든 프로세스 간 통신 및 동시 처리가 제공됩니다 (DBMS가 모든 프로세스를 처리합니다). 그렇지 않으면 웹 서비스가 2 개의 다른 서버에서 실행중인 경우 어떤 서비스 인스턴스가 대기열을 호스팅 할 것인가, 두 서버 간의 통신을 어떻게 처리 할 것인가 등 – mbeckish

+0

이 답변이 왜 downvoted인지, 다른 사람들과 마찬가지로 좋지만 그는 생산자와 소비자가 서로 다른 두 개의 서버에 있다는 점을 가지고 있습니다. 나는 여전히 그 상황을 다르게 해결할 수 있다고 생각하지만 (소비자의 서버로 아이템을 밀어 넣음) 그의 솔루션은 문제에 대한 훨씬 간단한 해결책을 제공 할 것이다. 카운터 – tfrascaroli

1

ThreadPool으로는이 작업을 수행 할 수 없습니다. 작업자 스레드를 시작하는 정적 생성자가있을 수 있습니다. DoAsyncStuffHere은 작업 항목을 작업 대기열에 삽입 할 수 있으며 작업자 스레드는 대기열에 작업 할 항목이 있는지 확인할 수 있습니다. 그렇다면, 그것은 일을하고, 그렇지 않으면 몇 밀리 동안 잔다.

정적 생성자는 그것이 단 한 번만 호출되도록하고, 단 하나의 스레드 만 시작해야합니다. (내가 모르는 이상한 .NET 가장자리의 경우가 아니라면).

다음은 레이아웃 예제입니다. 큐에 잠금 기능을 구현하고 작업자 스레드에 좀 더 정교함을 추가해야하지만 성공하기 전에이 패턴을 사용했습니다. WorkItem 개체는 작업자 스레드로 전달할 정보의 상태를 유지할 수 있습니다.

public static WebService() 
{ 
    new Thread(WorkerThread).Start(); 
    WorkQueue = new Queue<WorkItem>(); 
} 

public static void WorkerThread() 
{ 
    while(true) 
    { 
    if(WorkQueue.Any()) 
    { 
     WorkQueue.Dequeue().DoWork(); 
    } 
    else 
    { 
     Thread.Sleep(100); 
    } 
    } 
} 

public static Queue<WorkItem> WorkQueue { get; set; } 

[System.Web.Services.WebMethod] 
public List<Foo> SampleWebMethod(string id) 
{ 
    WorkQueue.Queue(newWorkItem()); 
} 
+0

을 재설정하기 위해 upvoted 이것은 완벽하게 좋은 해결책입니다. 내가 대답을 선택할 때 didnt 한 유일한 이유는 실제로 받아 들여지는 대답이 실제로 빌드 된 것입니다. 그래도 고마워. – user163757

+0

고마워요 - @ phoog의 대답은 * 더 좋은 방법이고 지금 배웠습니다. – eouw0o83hf