NET define a funcionalidade multithreading no namespace System.Threading. Portanto, para usar multithreading, você deve primeiro declarar uma referência a esse namespace (usando System.Threading;).
a. Iniciar um thread. Como o nome sugere, "iniciar um thread" significa criar e iniciar um thread.
Thread thread1 = new Thread(new ThreadStart(Count));
Onde Count é a função a ser executada pelo novo thread.
b.
"Matar um thread" é erradicar um thread. Para não desperdiçar seus esforços, é melhor determinar se ele ainda está ativo (por meio do atributo IsAlive) antes de encerrar um thread e, em seguida, chamar o método Abort para encerrar o thread. .
c. Pausar o thread significa deixar um thread em execução hibernar por um período de tempo. Por exemplo, thread.Sleep(1000); é deixar o thread dormir por 1 segundo.
d. A prioridade não precisa de explicação. O atributo hreadPriority na classe Thread é usado para definir a prioridade, mas não há garantia de que o sistema operacional aceitará a prioridade. A prioridade de um thread pode ser dividida em 5 tipos: Normal, Acima do Normal, Abaixo do Normal, Mais Alta e Mais Baixa. Exemplos específicos de implementação são os seguintes:
thread.Priority = ThreadPriority.Highest;
e. Suspender tópico
O método Suspend da classe Thread é usado para suspender o thread até que Resume seja chamado antes que o thread possa continuar a ser executado. Se o tópico já estiver suspenso, isso não funcionará.
if (thread.ThreadState = ThreadState.Running)
{
thread.Suspend();
}
f. O thread de recuperação é usado para retomar o thread suspenso para que ele possa continuar a execução. Se o thread não for suspenso, ele não funcionará.
if (thread.ThreadState = ThreadState.Suspended)
{
thread.Resume();
}
Um exemplo está listado abaixo para ilustrar a funcionalidade simples de threading. Este exemplo vem da documentação de ajuda.
usando o sistema;
usando System.Threading;
// Cenário de threading simples: inicia um método estático em execução
// em um segundo thread.
classe pública ThreadExample {
// O método ThreadProc é chamado quando o thread é iniciado.
// Ele faz um loop dez vezes, escrevendo no console e retornando
// o restante do intervalo de tempo a cada vez e então termina.
public static void ThreadProc() {
for (int i = 0; i < 10; i++) {
Console.WriteLine("ThreadProc: {0}", i);
// Produz o resto do intervalo de tempo.
Thread.Sleep(0);
}
}
public static void Main() {
Console.WriteLine("Thread principal: Iniciar um segundo thread.");
// O construtor da classe Thread requer um ThreadStart
// delegado que representa o método a ser executado no
// thread. C# simplifica a criação deste delegado.
Tópico t = novo Tópico(novo ThreadStart(ThreadProc));
// Inicia ThreadProc Em um uniprocessador, o thread não é obtido.
// qualquer tempo do processador até que o thread principal seja gerado. Remova o comentário.
// o Thread.Sleep que segue t.Start() para ver a diferença.
t.Iniciar();
//Thread.Sleep(0);
for (int i = 0; i < 4; i++) {
Console.WriteLine("Tópico principal: Faça algum trabalho.");
Thread.Sleep(0);
}
Console.WriteLine("Thread principal: Chame Join(), para esperar até que ThreadProc termine.");
t.Join();
Console.WriteLine("Thread principal: ThreadProc.Join retornou. Pressione Enter para finalizar o programa.");
Console.ReadLine();
}
}
Este código produz uma saída semelhante à seguinte:
Tópico principal: inicie um segundo tópico.
Tópico principal: Faça algum trabalho.
ThreadProc: 0
Tópico principal: Faça algum trabalho.
ThreadProc: 1
Tópico principal: Faça algum trabalho.
ThreadProc: 2
Tópico principal: Faça algum trabalho.
ThreadProc: 3
Thread principal: Chame Join(), para esperar até que ThreadProc termine.
ThreadProc: 4
ThreadProc: 5
ThreadProc: 6
ThreadProc: 7
ThreadProc: 8
ThreadProc: 9
Tópico principal: ThreadProc.Join retornou. Pressione Enter para finalizar o programa.
No Visul C#, o namespace System.Threading fornece algumas classes e interfaces que permitem a programação multithread. Existem três métodos para criar threads: Thread, ThreadPool e Timer. Aqui está uma breve introdução sobre como usá-los um por um.
1. Tópico
Este é talvez o método mais complexo, mas fornece uma variedade de controles flexíveis sobre threads. Primeiro, você deve usar seu construtor para criar uma instância de thread. Seus parâmetros são relativamente simples, com apenas um delegado ThreadStart: public Thread(ThreadStart start); para definir ou obter sua prioridade de execução (enum ThreadPriority: Normal, Menor, Mais Alto, Abaixo do Normal, Acima do Normal).
O exemplo a seguir primeiro gera duas instâncias de thread t1 e t2, depois define suas prioridades respectivamente e, em seguida, inicia os dois threads (os dois threads são basicamente iguais, exceto que sua saída é diferente, t1 é "1" e t2 é "2 ", De acordo com a proporção do número de caracteres que eles produzem, podemos ver aproximadamente a proporção do tempo de CPU que ocupam, o que também reflete suas respectivas prioridades).
vazio estático principal(string[] args)
{
Tópico t1 = novo Tópico(novo ThreadStart(Thread1));
Tópico t2 = novo Tópico(novo ThreadStart(Thread2));
t1.Priority = ThreadPriority.BelowNormal;
t2.Priority = ThreadPriority.Lowest;
t1.Iniciar();
t2.Iniciar();
}
público estático vazio Thread1()
{
para (int i = 1; i < 1000; i++)
{//Escreva um "1" sempre que um loop for executado.
dosth();
Console.Write("1");
}
}
vazio estático público Thread2()
{
para (int i = 0; i < 1000; i++)
{//Escreva um "2" sempre que um loop for executado.
dosth();
Console.Write("2");
}
}
public static void dosth()
{//Usado para simular operações complexas
para (int j = 0; j < 10000000; j++)
{
int a=15;
uma = a*a*a*a;
}
}
O resultado da execução do programa acima é:
11111111111111111111111111111111111111121111111111111111111111111111111111111112
11111111111111111111111111111111111111121111111111111111111111111111111111111112
11111111111111111111111111111111111111121111111111111111111111111111111111111112
A partir dos resultados acima, podemos ver que o thread t1 ocupa muito mais tempo de CPU do que t2. Isso ocorre porque a prioridade de t1 é maior que a de t2. Se definirmos as prioridades de t1 e t2 como Normal, os resultados. são como segue imagem:
121211221212121212121212121212121212121212121212121212121212121212121
212121212121212121212121212121212121212121212121212121212121212121212
121212121212121212
No exemplo acima, podemos ver que sua estrutura é semelhante ao thread de trabalho win32, mas é mais simples. Você só precisa usar a função a ser chamada pelo thread como delegado e, em seguida, usar o delegado como parâmetro para. construir uma instância de thread. Quando Start() é chamado para iniciar, a função correspondente será chamada e a execução começará a partir da primeira linha dessa função.
A seguir, combinamos a propriedade ThreadState do thread para entender o controle do thread. ThreadState é um tipo de enumeração que reflete o estado do thread. Quando uma instância de Thread é criada pela primeira vez, seu ThreadState é Unstarted; quando o thread é iniciado chamando Start(), seu ThreadState é Running após o thread ser iniciado, se você quiser pausar (bloquear), você pode chamar Thread. Método Sleep(), possui dois métodos sobrecarregados (Sleep(int), Sleep(Timespan)), que são apenas formatos diferentes para representar a quantidade de tempo. Quando esta função é chamada em um thread, significa que o thread irá bloquear. por um período de tempo (o tempo é determinado pelo número de milissegundos ou Timespan passado para Sleep, mas se o parâmetro for 0, significa suspender este thread para permitir a execução de outros threads, especificando Infinite para bloquear o thread indefinidamente), em desta vez, seu ThreadState se tornará WaitSleepJoin. Outra coisa digna de nota é que a função Sleep() é definida como estática. ! Isso também significa que não pode ser usado em conjunto com uma instância de thread, ou seja, não há chamada semelhante a t1.Sleep(10)! Assim, a função Sleep() só pode ser chamada pelo thread que precisa "Sleep" e não pode ser chamada por outros threads. Assim como quando dormir é um assunto pessoal que não pode ser decidido por outros. . Mas quando um thread está no estado WaitSleepJoin e precisa ativá-lo, você pode usar o método Thread.Interrupt, que lançará uma ThreadInterruptedException no thread. Vejamos primeiro um exemplo (observe o método de chamada de Sleep):
vazio estático principal(string[] args)
{
Tópico t1 = novo Tópico(novo ThreadStart(Thread1));
t1.Iniciar();
t1.Interrupção();
E.WaitOne();
t1.Interrupção();
t1.Join();
Console.WriteLine(“t1 é fim”);
}
AutoResetEvent estático E = novo AutoResetEvent (falso);
público estático vazio Thread1()
{
tentar
{//Pode-se ver pelos parâmetros que isso causará hibernação
Thread.Sleep(Timeout.Infinito);
}
catch(System.Threading.ThreadInterruptedException e)
{//Manipulador de interrupções
Console.WriteLine ("1ª interrupção");
}
E.Set();
tentar
{// dormir
Thread.Sleep(Timeout.Infinito);
}
catch(System.Threading.ThreadInterruptedException e)
{
Console.WriteLine ("2ª interrupção");
}//Pausa por 10 segundos
Thread.Sleep (10000);
}
O resultado da execução é: 1ª interrupção
2ª interrupção
(Depois dos 10s)t1 é o fim
No exemplo acima, podemos ver que o método Thread.Interrupt pode despertar o programa de um estado de bloqueio (WaitSleepJoin) e inserir o manipulador de interrupção correspondente e, em seguida, continuar a execução (seu ThreadState também muda para Running). função deve observar os seguintes pontos:
1. Este método pode não apenas despertar o bloqueio causado pelo Sleep, mas também é eficaz para todos os métodos que podem fazer com que o thread entre no estado WaitSleepJoin (como Wait e Join). Conforme mostrado no exemplo acima, ao usá-lo, você deve colocar o método que causa o bloqueio do thread no bloco try e colocar o manipulador de interrupção correspondente no bloco catch.
2. Chame Interrupt em um determinado thread. Se estiver no estado WaitSleepJoin, ele entrará no manipulador de interrupção correspondente para execução. Se não estiver no estado WaitSleepJoin neste momento, será interrompido imediatamente quando entrar neste estado posteriormente. . Se Interrupt for chamado várias vezes antes da interrupção, apenas a primeira chamada será válida. É por isso que usei a sincronização no exemplo acima, para garantir que a segunda chamada para Interrupt seja chamada após a primeira interrupção, caso contrário poderá causar a segunda. A chamada não tem efeito (se for chamada antes da primeira interrupção). Você pode tentar remover a sincronização. O resultado provavelmente será: 1ª interrupção.
O exemplo acima também usa dois outros métodos para fazer o thread entrar no estado WaitSleepJoin: usando o objeto de sincronização e o método Thread.Join. O uso do método Join é relativamente simples. Isso significa que o thread atual que chama esse método é bloqueado até que o outro thread (t1 neste exemplo) termine ou o tempo especificado passe (se também usar um parâmetro de tempo). condição (se houver), ele encerra imediatamente o estado WaitSleepJoin e entra no estado Running (a condição pode ser determinada com base no valor de retorno do método .Join. Se for verdadeiro, o thread será encerrado; se for falso, o tempo acabou). O thread também pode ser suspenso usando o método Thread.Suspend. Quando um thread está no estado Running e o método Suspend é chamado nele, ele entrará no estado SuspendRequested, mas não será suspenso imediatamente até que o thread atinja um estado seguro. ponto. O thread trava e nesse ponto ele entrará no estado Suspenso. Se chamado em um thread que já está suspenso, será inválido. Para retomar a operação, basta chamar Thread.Resume.
A última coisa sobre a qual falamos é a destruição de threads. Podemos chamar o método Abort no thread que precisa ser destruído e ele lançará uma ThreadAbortException neste thread. Podemos colocar algum código do thread no bloco try e colocar o código de processamento correspondente no bloco catch correspondente. Quando o thread estiver executando o código no bloco try, se Abort for chamado, ele irá pular para o bloco catch correspondente. . Executado dentro do bloco catch, ele terminará após a execução do código no bloco catch (se ResetAbort for executado dentro do bloco catch, será diferente: cancelará a solicitação Abort atual e continuará a execução para baixo. Portanto, se você quiser garantir que um thread termine, é melhor usar Join , como no exemplo acima).
2. ThreadPool
Thread Pool (ThreadPool) é um método relativamente simples. É adequado para tarefas curtas que requerem vários threads (como alguns threads que são frequentemente bloqueados). Como cada processo possui apenas um pool de threads e, é claro, cada domínio de aplicativo possui apenas um pool de threads (linha), você descobrirá que as funções-membro da classe ThreadPool são todas estáticas! Quando você chama ThreadPool.QueueUserWorkItem, ThreadPool.RegisterWaitForSingleObject, etc. pela primeira vez, uma instância do pool de threads será criada. A seguir está uma introdução às duas funções no pool de threads:
public static bool QueueUserWorkItem( //Retorna verdadeiro se a chamada for bem-sucedida
WaitCallback callBack,//O delegado chamado pelo thread a ser criado
estado do objeto //Parâmetros passados para o delegado
)//Sua outra função sobrecarregada é semelhante, exceto que o delegado não aceita parâmetros. A função desta função é enfileirar o thread a ser criado no pool de threads quando o número de threads disponíveis no pool de threads não for zero ( o pool de threads criou threads Se o número for limitado (o valor padrão é 25), este thread será criado, caso contrário, ele será enfileirado no pool de threads e aguardará até que tenha threads disponíveis.
public static RegisteredWaitHandle RegisterWaitForSingleObject(
WaitHandle waitObject, //WaitHandle a ser registrado
WaitOrTimerCallback callBack, // Delegado de chamada de thread
estado do objeto, // Parâmetros passados para o delegado
int TimeOut, //Timeout, unidade é milissegundos,
bool executeOnlyOnce file:// Se deve ser executado apenas uma vez
);
delegado público void WaitOrTimerCallback(
estado do objeto, // ou seja, os parâmetros passados para o delegado
bool timedOut // true significa devido à chamada de tempo limite, caso contrário significa waitObject
);
A função desta função é criar um thread de espera. Uma vez chamada esta função, este thread será criado em um estado "bloqueado" até que o parâmetro waitObject mude para o estado finalizado ou o tempo definido TimeOut chegue. vale a pena notar que este "Bloqueio" é muito diferente do estado WaitSleepJoin do Thread: quando um Thread está no estado WaitSleepJoin, a CPU irá acordá-lo regularmente para pesquisar e atualizar as informações de status e, em seguida, entrar no estado WaitSleepJoin novamente. A troca de thread consome muitos recursos; e o thread criado com esta função é diferente. A CPU não mudará para este thread antes de ser acionado para ser executado. sabe quando executá-lo? Na verdade, o pool de threads irá gerar alguns threads auxiliares para monitorar essas condições de gatilho. Assim que as condições forem atendidas, os threads correspondentes serão iniciados. É claro que esses threads auxiliares também levarão tempo, mas se você precisar criar mais espera. threads, use as vantagens do pool de threads. Veja exemplo abaixo:
AutoResetEvent estático ev = novo AutoResetEvent (falso);
public static int Principal(string[] args)
{ThreadPool.RegisterWaitForSingleObject(
ev,
novo WaitOrTimerCallback (WaitThreadFunc),
4,
2000,
false//Indica que o cronômetro será zerado toda vez que a operação de espera for concluída até o logout e a espera.
);
ThreadPool.QueueUserWorkItem (novo WaitCallback (ThreadFunc),8);
Thread.Sleep (10000);
retornar 0;
}
público estático vazio ThreadFunc (objeto b)
{ Console.WriteLine ("o objeto é {0}",b);
para(int i=0;i<2;i++)
{ Thread.Sleep (1000);
ev.Set();
}
}
public static void WaitThreadFunc(objeto b,bool t)
{ Console.WriteLine ("o objeto é {0},t é {1}",b,t);
}
O resultado de sua operação é:
o objeto é 8
o objeto é 4,t é falso
o objeto é 4,t é falso
o objeto é 4,t é verdadeiro
o objeto é 4,t é verdadeiro
o objeto é 4,t é verdadeiro
A partir dos resultados acima, podemos ver que o thread ThreadFunc foi executado uma vez e WaitThreadFunc foi executado 5 vezes. Podemos julgar o motivo para iniciar este thread a partir do parâmetro bool t em WaitOrTimerCallback: se t for falso, significa waitObject, caso contrário, é devido ao tempo limite. Além disso, também podemos passar alguns parâmetros para a thread através do objeto b.
3. Temporizador
É adequado para métodos que precisam ser chamados periodicamente. Não é executado no thread que criou o temporizador. Ele é executado em um thread separado alocado automaticamente pelo sistema. Isso é semelhante ao método SetTimer no Win32. Sua estrutura é:
temporizador público (
TimerCallback callback, //Método a ser chamado
estado do objeto, // parâmetros passados para callback
int dueTime, //Quanto tempo levará para começar a chamar o retorno de chamada?
int period //O intervalo de tempo para chamar este método
); // Se dueTime for 0, o callback executa sua primeira chamada imediatamente. Se dueTime for Infinite, o retorno de chamada não chamará seu método. O cronômetro está desabilitado, mas pode ser reativado usando o método Change. Se período for 0 ou Infinito e dueTime não for Infinito, o retorno de chamada chamará seu método uma vez. O comportamento periódico do temporizador está desabilitado, mas pode ser reativado usando o método Change. Se período for zero (0) ou Infinito e dueTime não for Infinito, o retorno de chamada chamará seu método uma vez. O comportamento periódico do temporizador está desabilitado, mas pode ser reativado usando o método Change.
Se quisermos alterar o período e o dueTime do timer após criá-lo, podemos alterá-lo chamando o método Change do Timer:
bool público Mudança(
int dueTime,
período interno
);//Obviamente os dois parâmetros alterados correspondem aos dois parâmetros no Timer
public static int Principal(string[] args)
{
Console.WriteLine ("período é 1000");
Temporizador tm=novo temporizador (novo TimerCallback (TimerCall),3.1000.1000);
Thread.Sleep (2000);
Console.WriteLine ("período é 500");
tm.Alterar (0.800);
Thread.Sleep (3000);
retornar 0;
}
público estático void TimerCall (objeto b)
{
Console.WriteLine ("timercallback; b é {0}",b);
}
O resultado de sua operação é:
período é 1000
retorno de chamada do temporizador; b é 3
retorno de chamada do temporizador; b é 3
período é 500
retorno de chamada do temporizador; b é 3
retorno de chamada do temporizador; b é 3
retorno de chamada do temporizador; b é 3
retorno de chamada do temporizador; b é 3
Resumir
A partir da breve introdução acima, podemos ver as ocasiões em que eles são usados, respectivamente: Thread é adequado para ocasiões que exigem controle complexo de threads. ThreadPool é adequado para tarefas curtas que requerem vários threads (como alguns que são frequentemente bloqueados). ); Timer é adequado para métodos que precisam ser chamados periodicamente. Contanto que compreendamos suas características de uso, podemos escolher bem o método apropriado.