Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

457
Vistas
Problema de subprocesamiento múltiple con semáforo

Necesito tener la pieza de código que permitió ejecutar solo por 1 subproceso al mismo tiempo según la clave de parámetro:

 private static readonly ConcurrentDictionary<string, SemaphoreSlim> Semaphores = new(); private async Task<TModel> GetValueWithBlockAsync<TModel>(string valueKey, Func<Task<TModel>> valueAction) { var semaphore = Semaphores.GetOrAdd(valueKey, s => new SemaphoreSlim(1, 1)); try { await semaphore.WaitAsync(); return await valueAction(); } finally { semaphore.Release(); // Exception here - System.ObjectDisposedException if (semaphore.CurrentCount > 0 && Semaphores.TryRemove(valueKey, out semaphore)) { semaphore?.Dispose(); } } }

De vez en cuando recibí el error:

 The semaphore has been disposed. : System.ObjectDisposedException: The semaphore has been disposed. at System.Threading.SemaphoreSlim.CheckDispose() at System.Threading.SemaphoreSlim.Release(Int32 releaseCount) at Project.GetValueWithBlockAsync[TModel](String valueKey, Func`1 valueAction)

Todos los casos que puedo imaginar aquí son seguridad de subprocesos. Por favor ayuda, ¿qué caso me perdí?

over 4 years ago · Santiago Trujillo
2 Respuestas
Responde la pregunta

0

Tiene una carrera de subprocesos aquí, donde otra tarea está tratando de adquirir el mismo semáforo, y lo adquiere cuando lo Release , es decir, otro subproceso está esperando el semaphore.WaitAsync() . La verificación contra CurrentCount es una condición de carrera, y podría ir en cualquier dirección dependiendo del tiempo. La verificación de TryRemove es irrelevante, ya que el subproceso de la competencia ya obtuvo el semáforo; después de todo, estaba esperando WaitAsync() .

over 4 years ago · Santiago Trujillo Denunciar

0

Como se discutió en los comentarios, aquí tienes un par de condiciones de carrera.

  1. El subproceso 1 mantiene el bloqueo y el subproceso 2 está esperando WaitAsync() . El subproceso 1 libera el bloqueo y luego verifica semaphore.CurrentCount antes de que el subproceso 2 pueda adquirirlo.
  2. El subproceso 1 retiene el bloqueo, lo libera y verifica semaphore.CurrentCount que pasa. El subproceso 2 ingresa a GetValueWithBlockAsync , llama a Semaphores.GetOrAdd y obtiene el semáforo. El subproceso 1 luego llama a Semaphores.TryRemove y desecha el semáforo.

Realmente necesita bloquear la decisión de eliminar una entrada de Semaphores ; no hay forma de evitar esto. Tampoco tiene una forma de rastrear si algún subproceso ha obtenido un semáforo de Semaphores (y lo está esperando actualmente o aún no ha llegado a ese punto).

Una forma es hacer algo como esto: tener un bloqueo que se comparte entre todos, pero que solo se necesita al buscar/crear un semáforo y decidir si se desecha. Realizamos un seguimiento manual de cuántos subprocesos tienen actualmente interés en un semáforo en particular. Cuando un subproceso ha liberado el semáforo, adquiere el bloqueo compartido para verificar si alguien más tiene interés en ese semáforo y lo elimina solo si nadie más lo tiene.

 private static readonly object semaphoresLock = new(); private static readonly Dictionary<string, State> semaphores = new(); private async Task<TModel> GetValueWithBlockAsync<TModel>(string valueKey, Func<Task<TModel>> valueAction) { State state; lock (semaphoresLock) { if (!semaphores.TryGetValue(valueKey, out state)) { state = new(); semaphores[valueKey] = state; } state.Count++; } try { await state.Semaphore.WaitAsync(); return await valueAction(); } finally { state.Semaphore.Release(); lock (semaphoresLock) { state.Count--; if (state.Count == 0) { semaphores.Remove(valueKey); state.Semaphore.Dispose(); } } } } private class State { public int Count { get; set; } public SemaphoreSlim Semaphore { get; } = new(1, 1); }

La otra opción, por supuesto, es dejar que Semaphores crezca. Tal vez tenga una operación periódica para realizar y borrar cualquier cosa que no se esté utilizando, pero esto, por supuesto, deberá protegerse para garantizar que un subproceso no se interese repentinamente en un semáforo que se está limpiando.

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda