He generado las clases proxy para un servicio web en vusual studio con 'Agregar referencia web'. La clase RTWebService
generada tiene un método SetValueAsync
. Amplié esta clase y agregué un SetValueRequest
que realiza un seguimiento de las solicitudes y cancela todas las solicitudes pendientes cuando ocurre un error. Con cada petición que almacenar el objeto userState
en un ArrayList creé la siguiente manera:¿Por qué no funciona el contenedor sincronizado para ArrayList?
requests = ArrayList.Synchronized(new ArrayList());
creé un método:
public void CancelPendingRequests() {
lock (requests.SyncRoot) {
if (requests.Count > 0) {
foreach (object request in requests) {
this.CancelAsync(request);
}
requests.Clear();
}
}
}
que llamo este método cuando una solicitud de beneficios dentro del evento SetValueCompleted
:
private void onRequestComplete(
object sender,
Service.SetValueCompletedEventArgs args
) {
lock (syncResponse) {
if (args.Cancelled) {
return;
}
if (args.UserState != null) {
requests.Remove(args.UserState);
}
if (args.Error != null) {
CancelPendingRequests();
}
}
}
Para iniciar una nueva solicitud que llamo:
public void SetValueRequest(string tag, string value) {
var request = new object();
this.SetValueAsync(tag, value, request);
requests.Add(request);
}
Cada vez que realizo una solicitud y, al mismo tiempo, una respuesta regresa con un error, obtengo un TargetInvocationException
en el CancelPendingRequests
. La excepción interna es un InvalidOperationException
en un ArrayList en el CancelPendingRequests
método dicho:
Collection se modificó; la operación de enumeración no se puede ejecutar.
por lo que parece SetValueRequest
ha modificado el objeto requests
mientras yo estaba enumerando ella. Pensé que esto era imposible porque utilicé el contenedor sincronizado para ArrayList y usé SyncRoot para sincronizar la enumeración. Estoy un poco atrapado en esto así que si alguien tiene una idea?
tu código aún está roto. the for no gritará fuerte si cambia la lista mientras la itera, pero aún tendrá errores de condiciones de carrera. –
De hecho, eso es lo que quise decir, la pregunta es ** ¿por qué? ¿Por qué se cambia la lista a pesar de que la sincronizo con 'requests.SyncRoot'. No eliminaré elementos para que este no se rompa. – Jan
1. la excepción lanzada claramente significa que estás equivocado, la lista está siendo manipulada mientras iteras. 2. ArrayList.Synchronized solo sincroniza acciones individuales, no transacciones lógicas. obtener el enumerador de la lista está sincronizado, pero una vez que toca la lista, todos los enumeradores anteriores se rompen. hay una manera de arreglar esto dentro de ArrayList, puedes hacerlo a través de SyncRoot, pero no deberías, porque es un mecanismo roto –