Partilhar via


Threads de trabalho do sistema

Um driver que requer processamento atrasado pode usar um item de trabalho, que contém um ponteiro para uma rotina de chamada de retorno de driver que executa o processamento real. O driver enfileira o item de trabalho e um thread de sistema remove o item de trabalho da fila e executa a rotina de callback do driver. O sistema mantém um conjunto destes threads de trabalho do sistema, que são threads projetados para processar cada item de trabalho de forma individual.

O driver associa uma rotina de retorno de chamada WorkItem ao item de trabalho. Quando o thread de trabalho do sistema processa o item de trabalho, ele chama a rotina WorkItem associada. No Windows Vista e versões posteriores do Windows, um driver pode, em vez disso, associar uma rotina WorkItemEx a um item de trabalho. WorkItemEx usa parâmetros que são diferentes dos parâmetros que WorkItem toma.

As rotinas WorkItem e WorkItemEx são executadas em um contexto de thread do sistema. Se uma rotina de despacho de um driver puder ser executada num contexto de thread de modo utilizador, essa rotina poderá chamar uma rotina WorkItem ou WorkItemEx para executar quaisquer operações que exijam um contexto de thread do sistema.

Para usar um item de trabalho, um driver executa as seguintes etapas:

  1. Alocar e inicializar um novo item de trabalho.

    O sistema usa uma estrutura IO_WORKITEM para armazenar um item de trabalho. Para alocar uma nova estrutura de IO_WORKITEM e inicializá-la como um item de trabalho, o driver pode chamar IoAllocateWorkItem. No Windows Vista e versões posteriores do Windows, o driver pode alternativamente alocar sua própria estrutura de IO_WORKITEM e chamar IoInitializeWorkItem para inicializar a estrutura como um item de trabalho. (O driver deve chamar IoSizeofWorkItem para determinar o número de bytes necessários para armazenar um item de trabalho.)

  2. Associe uma rotina de retorno de chamada ao item de trabalho e enfileire o item de trabalho para que ele seja processado por um thread de trabalho do sistema.

    Para associar uma rotina WorkItem ao item de trabalho e enfileirar o item de trabalho, o driver deve chamar IoQueueWorkItem. Em vez disso, para associar uma rotina WorkItemEx ao item de trabalho e enfileirar o item de trabalho, o driver deve chamar IoQueueWorkItemEx.

  3. Depois que o item de trabalho não for mais necessário, libere-o.

    Um item de trabalho que foi alocado por IoAllocateWorkItem deve ser liberado por IoFreeWorkItem. Um item de trabalho que foi inicializado por IoInitializeWorkItem deve ser não inicializado por IoUninitializeWorkItem antes de poder ser liberado.

    O item de trabalho só pode ser não inicializado ou liberado quando o item de trabalho não está atualmente na fila. O sistema coloca o item de trabalho na fila antes de chamar a rotina de retorno de chamada do item de trabalho, portanto, IoFreeWorkItem e IoUninitializeWorkItem podem ser chamados de dentro do retorno de chamada.

Um DPC que precisa iniciar uma tarefa de processamento que requer processamento demorado ou que faz uma chamada de bloqueio deve delegar o processamento dessa tarefa a um ou mais itens de trabalho. Enquanto um DPC é executado, todos os threads são impedidos de serem executados. Além disso, um DPC, que é executado em IRQL = DISPATCH_LEVEL, não deve fazer chamadas de bloqueio. No entanto, o thread de trabalho do sistema que processa um item de trabalho é executado em IRQL = PASSIVE_LEVEL. Assim, o item de trabalho pode conter chamadas de bloqueio. Por exemplo, uma thread de trabalho do sistema pode aguardar num objeto dispatcher.

Como o pool de threads de trabalho do sistema é um recurso limitado, as rotinas WorkItem e WorkItemEx podem ser usadas apenas para operações que levam um curto período de tempo. Se uma dessas rotinas for executada por muito tempo (se contiver um loop indefinido, por exemplo) ou esperar por muito tempo, o sistema pode ficar bloqueado. Portanto, se um driver requer longos períodos de processamento atrasado, ele deve, em vez disso, chamar PsCreateSystemThread para criar seu próprio thread de sistema.

Não chame IoQueueWorkItem ou IoQueueWorkItemEx para enfileirar um item de trabalho que já esteja na fila. Fazer isso pode causar corrupção das estruturas de dados do sistema. Se o seu driver enfileirar o mesmo item de trabalho sempre que uma rotina específica do driver for executada, pode utilizar a seguinte técnica para evitar enfileirar o item de trabalho uma segunda vez se ele já estiver na fila:

  • O motorista mantém uma lista de tarefas para a rotina do trabalhador.
  • Esta lista de tarefas está disponível no contexto fornecido à rotina do trabalhador. A rotina do trabalhador e quaisquer rotinas de driver que modificam a lista de tarefas sincronizam seu acesso à lista.
  • Cada vez que a rotina do trabalhador é executada, ele executa todas as tarefas na lista e remove cada tarefa da lista à medida que a tarefa é concluída.
  • Quando uma nova tarefa chega, o driver adiciona essa tarefa à lista. O driver enfileira o item de trabalho somente se a lista de tarefas estava vazia anteriormente.

O thread de trabalho do sistema remove o item de trabalho da fila antes de chamar o thread de trabalho. Desta forma, um thread do controlador pode enfileirar com segurança o item de trabalho novamente assim que o worker thread começar a ser executado.