public bool Enter() { // ainda há espaço if (!IsRegionFull()) { if (maxInside > 0) { this.maxInside--; return(true); // a entrada foi feita com sucesso } } /* não há espaço, vou ver se posso ficar em espera na waitingQueue * não podem estar mais do que maxWaiting threads à espera de entrar na * zona protegida pela mesma chave */ if (IsWaitingQueueFull()) { return(false); } else { LinkedListNode <bool> node = waitingQueue.AddLast(false); int timeout = waitTimeout; int lastTime = (timeout != Timeout.Infinite) ? Environment.TickCount : 0; do { try { Monitor.Wait(myLock, timeout); } // interrompido o bloqueio da thread catch (ThreadInterruptedException e) { waitingQueue.Remove(node); throw; } // verificar se já fui sinalizado if (node.Value) { waitingQueue.Remove(node); if (maxInside > 0) { this.maxInside--; return(true); } } /* verificar se ocorreu timeout, uma thread não poderá esperar mais * do que waitTimeout milésimos de segundo para entrar na zona * protegida */ if (SyncUtils.AdjustTimeout(ref lastTime, ref timeout) == 0) { waitingQueue.Remove(node); return(false); } } while (true); } }
public T Exchange(T mine, int timeout) // throws ThreadInterruptedException { lock (myLock) { if (someoneIsWaiting) { this.mail.secondMessage = mine; this.mail.completed = true; someoneIsWaiting = false; Monitor.PulseAll(myLock); return(this.mail.firstMessage); } if (timeout == 0) { throw new TimeoutException(); } int lastTime = (timeout != Timeout.Infinite) ? Environment.TickCount : 0; mail = new Mail <T>(); mail.firstMessage = mine; mail.secondMessage = default(T); someoneIsWaiting = true; do { try { Monitor.Wait(myLock, timeout); } /* (c) a espera seja interrompida, terminado o método com o lançamento de ThreadInterruptedException. */ catch (ThreadInterruptedException) { if (mail.completed) { return(mail.secondMessage); } else { someoneIsWaiting = false; // vale a pena retiro a minha mensagem? visto q a proxima a entrar vai fazer default throw; } } /* (a) outra thread invoque o método Exchange , devolvendo o método a mensagem oferecida pela outra thread; */ if (mail.completed) { return(mail.secondMessage); } /* (b) expire o limite do tempo de espera especificado, situação em que o método devolve null , ou; */ if (SyncUtils.AdjustTimeout(ref lastTime, ref timeout) == 0) { someoneIsWaiting = false; throw new TimeoutException(); } } while (true); } }
// throws ThreadInterruptedException public bool TryEnter(int key) { lock (map) { //Se não existir a key, adiciona-se essa key com uma nova região if (!map.ContainsKey(key)) { map.Add(key, new Region()); } //região associada à key Region r = map[key]; //se a região não estiver cheia a thread pode entrar if (r.inside < maxInside) { //mais uma thread dentro da região r.inside++; //conseguiu entrar logo retorna true return(true); } //se a lista de espera para entrar na região não estiver cheia a thread pode ficar à espera if (r.waiting.Count < maxWaiting) { //Momento inicial da espera int lastTime = (waitTimeout != Timeout.Infinite) ? Environment.TickCount : 0; //Nova thread à espera - adiciona-se ao fim para cumprir a disciplina FIFO LinkedListNode <object> myRequest = r.waiting.AddLast(new object()); do { try { //Espera por waitTimeout ms SyncUtils.Wait(map, myRequest, waitTimeout); } catch (ThreadInterruptedException) { //Se saiu por excepção então removo a thread da lista de espera r.waiting.Remove(myRequest); //A expecção é relançada throw; } //Se a thread saiu por timeOut mas há espaço para entrar então entra na mesma if (r.inside < maxInside) { //Removo a thread da lista de espera r.waiting.Remove(myRequest); //Thread entra na região r.inside++; //Conseguiu entrar logo retorna true return(true); } //enquanto não tiver passado timeOut. Isto porque podem ocorrer saidas espurias } while (SyncUtils.AdjustTimeout(ref lastTime, ref waitTimeout) != 0); //Se a thread saiu por timeOut e não houve espaço para entrar na região então removo da lista de espera r.waiting.Remove(myRequest); //Lanço timeOut exception throw new TimeoutException(); } //Não conseguiu entrar na região nem na lista de espera então retorna false return(false); } }
public T Exchange(T mine, int timeout) { lock (lockObj) { //Se já existir um request então este request pode emparelhar com ele if (request != null) { //dois requests estão a emparelhar request.isExchanging = true; //Trocar mensagens T myMessage = request.message; request.message = mine; //Acorda a thread à espera de emparelhamento Monitor.Pulse(lockObj); //Retorna a mensagem return(myMessage); } //Ainda não existe nenhum request em espera de emparelhamento, logo tem de esperar por outro ThreadRequest req = new ThreadRequest(mine); request = req; //Momento inicial da espera int lastTime = (timeout != Timeout.Infinite) ? Environment.TickCount : 0; do { try { //Esperar por outro emparelhamento Monitor.Wait(lockObj, timeout); } catch (ThreadInterruptedException) { //Se sair por excepção e estiver a emparelhar: if (req.isExchanging) { //sinalizar esta thread como interrompida Thread.CurrentThread.Interrupt(); //retornar a mensagem return(req.message); } //Se sair por excepção e não estiver a emparelhar elimina o request e lança excepção request = null; throw; } //Se a thread acordou e estiver a emparelhar: if (req.isExchanging) { //Apagar o request request = null; //Retornar a mensagem return(req.message); } //enquanto não tiver passado timeOut. Isto porque podem ocorrer saidas espurias } while (SyncUtils.AdjustTimeout(ref lastTime, ref timeout) != 0); //Se a thread saiu por timeOut e está a emparelhar então continua e retorna if (req.isExchanging) { //Apagar o request request = null; //Retornar a mensagem return(req.message); } //Se a thread saiu por timeOut e não está a emparelhar então apaga o request e lança excepção request = null; throw new TimeoutException(); } }