В WCF настройка канала связи является довольно дорогостоящей операцией, поэтому рекомендуется установить один канал и делиться им в течение всего срока службы вашего приложения.Одно большое предостережение заключается в том, что если канал когда-либо выйдет из строя, то этот канал должен быть прерван и заменен, эти каналы зомби не могут быть воскрешены.
Я пытаюсь написать собственный прокси, который позаботится о сохраненииСоздан здоровый канал, обеспечивающий доступ к его операциям.Слепая часть возникает, когда канал выходит из строя, и мне нужно заменить его на новый, хороший канал в поточно-безопасном режиме.
Вот код, который у меня есть, я все еще немного ученик, когдаречь идет о черном искусстве многопоточного программирования.Является ли этот код излишним, недоразумением, просто неправильно?Можно ли сделать то же самое быстрее, проще или более правильно?
public class WcfDeviceActivationService : IDeviceActivationService {
ChannelFactory<IDeviceActivationService> _factory = new ChannelFactory<IDeviceActivationService>();
IDeviceActivationService _channel = null;
ReaderWriterLockSlim _swaplock = new ReaderWriterLockSlim();
public WcfDeviceActivationService() {
_channel = _factory.CreateChannel();
((IClientChannel)_channel).Open();
}
public Guid ActivateDevice(string activationCode, Guid userId) {
return Call(c => c.ActivateDevice(activationCode, userId) );
}
private RT Call<RT>(Func<IDeviceActivationService, RT> chanfunc) {
try {
using(_swaplock.UseReadLock())
return chanfunc(_channel);
} catch(Exception) {
// Get a reference to the channel before attempting the exclusive lock
var chan = _channel;
// Take an exclusive lock to block callers while we check the channel
// (just to prevent excessive failures under heavy call load)
using (_swaplock.UseWriteLock()) {
// Let's see if we're still working with the original channel and if it's faulted
if (Object.ReferenceEquals(chan, _channel) && ((IClientChannel)chan).State == CommunicationState.Faulted) {
// It faulted, so lets create a new channel to replace the bad one
// If the channel creation throws, the next attempt to use the failed channel
// will take this path again and attempt to create a fresh channel.
// We want the creation exception to propagate so that the caller
// knows that their call failed because the channel couldn't be created.
var newchan = _factory.CreateChannel();
((IClientChannel)newchan).Open();
// Exchange the new channel for the old one
// (assigning reference types is atomic, but Exchange also does a memory barrier for us)
Interlocked.Exchange(ref _channel, newchan);
// Clean up the old channel
((IClientChannel)chan).Abort();
}
}
// Propagate exception to the caller
throw;
}
}
}
ОБНОВЛЕНИЕ
Продумывая упрощение кода благодаря Фредрику и Млаву,Я рефакторинг на это, все еще не уверен, что это полностью правильно или нет.