Created
March 15, 2018 13:32
-
-
Save GuilhermeMatheus/8a26fecf3286ffba4819ecb7bc737249 to your computer and use it in GitHub Desktop.
Usando PauseTokenSource e PauseToken para notificar pausas em Tasks.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Threading; | |
| using System.Threading.Tasks; | |
| /// <summary> | |
| /// Classe de teste do PauseTokenSource e PauseToken | |
| /// </summary> | |
| class Program | |
| { | |
| static void Main() | |
| { | |
| var pts = new PauseTokenSource(); | |
| Task.Run(() => | |
| { | |
| while (true) | |
| { | |
| Console.WriteLine( | |
| pts.IsPaused | |
| ? "Task pausada. Dê enter para continuar sua execução..." | |
| : "Task executando. Dê enter para pausar sua execução..."); | |
| Console.ReadLine(); | |
| pts.IsPaused = !pts.IsPaused; | |
| } | |
| }); | |
| MetodoAssincronoDeExemploAsync(pts.Token).Wait(); | |
| } | |
| public static async Task MetodoAssincronoDeExemploAsync(PauseToken pause) | |
| { | |
| for (int i = 1; i < Int32.MaxValue; i++) | |
| { | |
| Console.WriteLine($"Sou uma Task executando um loop pela {i}º vez . . ."); | |
| await Task.Delay(1000); | |
| await pause.WaitWhilePausedAsync(); | |
| } | |
| } | |
| } | |
| /// <summary> | |
| /// A PauseTokenSource é a classe geradora de tokens de pausa, o PauseToken. | |
| /// A separação destas classes é importante para impedir que um usuário de PauseToken | |
| /// altere o valor de sua propriedade IsPaused enquanto o criador do token de pausa | |
| /// poderá sinalizar a pausa através do PauseTokenSource | |
| /// </summary> | |
| public class PauseTokenSource | |
| { | |
| // O modificador VOLATILE indica ao compilador que o campo | |
| // deve sempre ser operado na memória principal do computador. | |
| // Sem este modificador, é possível que este campo seja operado | |
| // na memória CACHE de um núcleo da CPU. Sendo assim, dois nucleos | |
| // operando simultaneamente neste campo podem ver valores diferentes. | |
| // Veremos que o setter de IsPaused se preocupa com isto. | |
| private volatile TaskCompletionSource<bool> m_paused; | |
| internal static readonly Task s_completedTask = Task.FromResult(true); | |
| // O token que indica se a tarefa deve ser pausada ou não! | |
| public PauseToken Token { get { return new PauseToken(this); } } | |
| // Nesta propriedade, podemos pausar ou continuar a execução | |
| // das tasks clientes do Token PauseToken | |
| public bool IsPaused | |
| { | |
| get { return m_paused != null; } | |
| set | |
| { | |
| if (value) | |
| { | |
| // A classe Interlocked é usada para operações atômicas. | |
| // Ou seja, o campo m_paused será acessado apenas por uma | |
| // thread por vez e não simultaneamente - o que pode causar um erro. | |
| // Para pausar, a lógica é definir a TaskCompletionSource m_paused para | |
| // um novo new TaskCompletionSource<bool>() | |
| Interlocked.CompareExchange( | |
| ref m_paused, new TaskCompletionSource<bool>(), null); | |
| } | |
| else | |
| { | |
| while (true) | |
| { | |
| var tcs = m_paused; | |
| if (tcs == null) return; | |
| // Para continuar, a lógica é definir a m_paused para nulo | |
| if (Interlocked.CompareExchange(ref m_paused, null, tcs) == tcs) | |
| { | |
| tcs.SetResult(true); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // O modificador internal é para o PauseToken ter visibilidade | |
| // deste método enquanto ele será invisível para outros projetos | |
| internal Task WaitWhilePausedAsync() | |
| { | |
| var cur = m_paused; | |
| return cur != null ? cur.Task : s_completedTask; | |
| } | |
| } | |
| /// <summary> | |
| /// O token de pausa é representado em uma Struct pois seus campos são | |
| /// todos de tipo de valor, é imutável, não é usado em operações de box/unboxing | |
| /// e não gera pressão no Garbage Collector. | |
| /// </summary> | |
| public struct PauseToken | |
| { | |
| private readonly PauseTokenSource m_source; | |
| internal PauseToken(PauseTokenSource source) { m_source = source; } | |
| // Apenas retorna a propriedade IsPaused de PauseTokenSource | |
| public bool IsPaused { get { return m_source != null && m_source.IsPaused; } } | |
| // Método auxiliar para retornar uma Task para podermos dar await enquanto | |
| // a aplicação está pausada | |
| public Task WaitWhilePausedAsync() | |
| { | |
| return IsPaused ? | |
| m_source.WaitWhilePausedAsync() : | |
| PauseTokenSource.s_completedTask; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment