2012-03-22 4 views
2

У меня есть интерфейс вроде этого:Можно ли скрыть механизм обратного вызова одним способом?

interface IAuthentication 
{ 
    void AuthenticateAsync(string user, string pwhash); 
    event EventHandler<AuthenticationResult> AuthenticationDone; 
} 

Это работает, поднимая событие, когда это будет сделано. Теперь я хотел бы, чтобы обернуть этот механизм внутри одного, блокирующий метод, который возвращает результат аутентификации после того, как это делается:

AuthenticationResult Authenticate(string user, string pwhash) 
{ 
    var auth = GetIAuthenticator(); 
    // ... do something 
    return <the authentication result from the even argument>; 
} 

Возможно ли это каким-то образом?

+0

Вы используете .NET 4.0? Если да, то вам поможет параллельная библиотека задач. – ken2k

+0

Да, я использую .NET 4.0. –

ответ

3

С ожиданием ручкой вам не нужно, чтобы проверить некоторые флаги, блок нитку и набор тайм-аут:

private AuthenticationResult Authenticate(string user, string pwhash) 
{    
    IAuthentication auth = GetIAuthenticator(); 
    AuthenticationResult result = null; 
    AutoResetEvent waitHangle = new AutoResetEvent(false); 

    auth.AuthenticationDone += (o, e) => 
     { 
      result = e; 
      waitHangle.Set(); 
     }; 

    auth.AuthenticateAsync(user, pwhash); 
    waitHangle.WaitOne(); // or waitHangle.WaitOne(interval); 
    return result; 
} 
+1

Как долго это будет ждать в случае поврежденного сетевого соединения/etc? – sll

+0

Вы можете использовать перегруженный метод WaitOne с максимальным интервалом ожидания. –

+1

Я думаю, что ему нужен интервал для блокировки вообще. Тем не менее, это действительно очень изящное решение. –

2
private AuthenticationResult Authenticate(string user, string pwhash) 
{ 
    bool isDone = false; 
    AuthenticationResult results = null 
    var auth = GetIAuthenticator(); 
    auth.AuthenticationDone += (o, e) => 
    { 
     isDone = true; 
     results = e; 
    }; 

    auth.AuthenticateAsync(user, pwhash); 

    long maxWaitTimeSeconds = 10; 
    long thresholdMilliseconds = 100; 
    int countToWait = maxWaitTimeSeconds * 1000/thresholdMilliseconds; 
    while (!isDone || countToWait-- > 0) 
    { 
     Thread.Sleep(thresholdMilliseconds); 
    } 

    if (countToWait == 0 && !isDone) 
    { 
     // TODO: timeout handling 
    } 

    return results;  
} 

PS: Если арг события никогда не может быть пустым - вы можете избавиться от isDone переменных и использовать только result != null в качестве индикатора «аутентификации сделано»

+0

делает ли лямбда переменной isDone ссылкой? –

+0

Если у вас есть другой поток и вы используете метод wait/notify, у вас будет более отзывчивость и более низкий расточительный опрос/прядение. – Servy

+0

@ TamásSzelei На самом деле это совсем не проходит; это просто доступ к той же переменной везде, где вы видите, что она используется здесь. – Servy

2

Как вы используете .NET 4.0, вы могли бы воспользоваться параллельной библиотекой задач.

Вот очень простая программа, которая показывает, как использовать TaskCompletionSource:

public class Test 
{ 
    public void Go() 
    { 
     ThreadPool.QueueUserWorkItem((z) => this.Imp()); 
    } 

    private void Imp() 
    { 
     Console.WriteLine("Asynchronous operation in progress (1/2)..."); 
     Thread.Sleep(2000); 
     Console.WriteLine("Asynchronous operation in progress (2/2)..."); 

     if (this.Done != null) 
     { 
      this.Done(this, EventArgs.Empty); 
     } 
    } 

    public event EventHandler Done; 
} 

internal class Program 
{ 
    private static void Main(string[] args) 
    { 
     Test test = new Test(); 

     TaskCompletionSource<object> tcs = new TaskCompletionSource<object>(null); 

     Console.WriteLine("Starting asynchronous operation"); 

     Task.Factory.StartNew(() => 
     { 
      test.Done += (sender, e) => tcs.SetResult(null); 
      test.Go(); 
     }); 

     // Blocking until completion of the async operation 
     var tmp = tcs.Task.Result; 

     Console.WriteLine("Asynchronous operation completed"); 

     Console.ReadKey(); 
    } 
} 

Результат:

Starting asynchronous operation 
Asynchronous operation in progress (1/2)... 
Asynchronous operation in progress (2/2)... 
Asynchronous operation completed 

Как вы можете видеть, поток выполнения блокируется до тех пор, асинхронная операция не завершится.

+0

Зачем вам нужен отдельный поток в случае синхронного запроса? – sll

+0

@sll Ничего не создано, но начата задача, которая не то же самое. TPL используется в качестве примера, чтобы использовать класс 'TaskCompletionSource'. – ken2k

+0

AFAIK TPL мог или не мог создать поток (в пуле) для работы async, я считаю, что вы не контролируете это, я не прав? BTW, ваш образец вывода говорит другое - «основная нить .. другая нить», что это значит? – sll

Смежные вопросы