2013-07-31 26 views
10

şunlardır:IProgress <T> senkronizasyon

İlk çalıştırma:

Result: 1001 
Progress: 4 
Progress: 6 
Progress: 7 
Progress: 8 
Progress: 9 
Progress: 3 
Progress: 0 
Progress: 1 
Progress: 5 
Progress: 2 

İkinci çalışma:

Progress: 4 
Progress: 5 
Progress: 6 
Progress: 7 
Progress: 8 
Progress: 9 
Progress: 0 
Progress: 1 
Progress: 2 
Result: 1001 
Progress: 3 

vb ...

Her çalışma için çıktı farklıdır. ilerleme, bunlar 0,1 bildirilmektedir sırayla görüntülenecek şekilde nasıl ... Bu yöntemleri senkronize edebilirsiniz 1001. Ben çıkışı böyle olmasını istediğim sonucu takiben 9:

Progress: 0 
. 
. 
. 
Progress: 9 
Result: 1001 

cevap

12

> sınıf mesaj() ilerleme güncellemesine SynchronizationContext.Current özelliğini kullanır. Bu, ProgressChanged olayının bir programın UI iş parçacığı üzerinde çalışmasını sağlamak için yapıldı, böylece kullanıcı arabirimini güncelleştirmek güvenlidir. ProgressBar.Value özelliğini güvenli bir şekilde güncellemek için gerekli.

Konsol modu uygulamasıyla ilgili sorun, bir senkronizasyon sağlayıcısına sahip olmamasıdır. Winforms veya WPF uygulaması gibi değil. Synchronization.Current özelliği varsayılan sağlayıcıya sahip, onun Post() yöntemi bir iş parçacığı iş parçacığı üzerinde çalışır. Herhangi bir kilitleme olmadan, TP iş parçacığının güncelleştirmeyi bildirmesi gereken ilk şey tamamen önceden kestirilemez. Ayrıca, kilitlenmenin iyi bir yolu yoktur.

Sadece İlerleme dersini kullanmayın, bir nokta yoktur. Konsol modu uygulamasında UI iş parçacığı güvenlik sorununuz yok, Konsol sınıfı zaten iş parçacığı için güvenli. Fix:

static int Foo() 
{ 
    for (int i = 0; i < 10; i++) 
     Console.WriteLine("Progress: {0}", i); 

    return 1001; 
} 
+0

Konsol uygulamasında, potansiyel olarak GUI uygulamasında (web uygulamaları dahil) kullanılacak bir kitaplık tasarlıyorum. Düşündüğüm başka bir alternatif, bu senaryoda çalışan bir temsilci Eylemi kullanmaktır, ancak lib'm bir GUI uygulamasından kullanılıyorsa bunun en iyi çözüm olup olmadığından emin değilim. Ne düşünüyorsun? – danze

+0

Bu kararı, bilmediğiniz bir uygulama için yapamazsınız, iş parçacığı gereksinimlerinin ne olduğu hakkında hiçbir fikriniz yoktur ve belirli bir iş parçacığında kullanıldığınızı varsayabilirsiniz. Öyleyse, bir etkinlik oluşturmayın ve istemci uygulamasının bununla ilgilenmesine izin vermeyin. –

+1

Aslında kod yazdığınız kadar kötü değil, Hans'ın dediği gibi, kütüphanenizde karar alamıyorsunuz, bu yüzden IProgress arayüzünü kullanmak kesinlikle iyi bir fikir. GUI uygulamalarından Progress uygulamasını kullanıyorsunuz, Console uygulamalarından kendi uygulamanızı kullanabilirsiniz. İsterseniz, IProgress uygulamanız yalnızca Action 'u sarabilir, böylece nasıl çalışırsınız. Olaylar için bunu tercih ederim, IProgress şu anda standart olarak görünüyor .. –

1

Bu Progress<T>'un nasıl yazıldığına dair bir iş parçacığı sorundur. İhtiyacınız olanı almak için kendi uygulamanızı IProgress<T> yazmanız gerekir.

Ancak, bu senaryoda size önemli bir şey anlatıyor, ancak bu örnekte, basit senaryolarda basit bir şekilde Console.Writeline ifadeleri yapıyorsunuz, gerçek senaryolarda, bazı raporlar daha uzun veya daha kısa sürdüğümden dolayı başka bir sırada raporlanabilir. Yine de emre güvenmemelisin.

İlerleme <
2

diğer cevaplar ile daha önce birkaç kez belirttiği gibi, bu Progress<T> nasıl uygulandığını nedeniyle bu. Müşterilerinize (kütüphane kullanıcıları) örnek bir kod veya bir konsol projesi için IProgress<T> uygulamasını sağlayabilirsiniz. Bu temel, ama yapmalı.

public class ConsoleProgress<T> : IProgress<T> 
{ 
    private Action<T> _action; 

    public ConsoleProgress(Action<T> action) { 
     if(action == null) { 
      throw new ArgumentNullException(nameof(action)); 
     } 

     _action = action; 
    } 

    public void Report(T value) { 
     _action(value); 
    } 
} 
4

olarak Hans' answer yılında, Progress<T> arasında .NET uygulaması kendi istekleri göndermek için SynchronizationContext.Post kullanmakta olduğunu belirtti. Doğrudan Yves' answer'da olduğu gibi raporlayabilir veya SynchronizationContext.Send kullanabilirsiniz, böylece istek alıcı işlenene kadar engellenir.

uygulamadan Reference Source is available kaynağını kopyalama ve Send için Post değişen ve SynchronizationContext.CurrentCurrentNoFlow nedeniyle bir iç özellik olma SynchronizationContext.CurrentNoFlow değiştirmek kadar kolay olduğu için.

/// <summary> 
/// Provides an IProgress{T} that invokes callbacks for each reported progress value. 
/// </summary> 
/// <typeparam name="T">Specifies the type of the progress report value.</typeparam> 
/// <remarks> 
/// Any handler provided to the constructor or event handlers registered with 
/// the <see cref="ProgressChanged"/> event are invoked through a 
/// <see cref="System.Threading.SynchronizationContext"/> instance captured 
/// when the instance is constructed. If there is no current SynchronizationContext 
/// at the time of construction, the callbacks will be invoked on the ThreadPool. 
/// </remarks> 
public class SynchronousProgress<T> : IProgress<T> 
{ 
    /// <summary>The synchronization context captured upon construction. This will never be null.</summary> 
    private readonly SynchronizationContext m_synchronizationContext; 
    /// <summary>The handler specified to the constructor. This may be null.</summary> 
    private readonly Action<T> m_handler; 
    /// <summary>A cached delegate used to post invocation to the synchronization context.</summary> 
    private readonly SendOrPostCallback m_invokeHandlers; 

    /// <summary>Initializes the <see cref="Progress{T}"/>.</summary> 
    public SynchronousProgress() 
    { 
     // Capture the current synchronization context. "current" is determined by Current. 
     // If there is no current context, we use a default instance targeting the ThreadPool. 
     m_synchronizationContext = SynchronizationContext.Current ?? ProgressStatics.DefaultContext; 
     Contract.Assert(m_synchronizationContext != null); 
     m_invokeHandlers = new SendOrPostCallback(InvokeHandlers); 
    } 

    /// <summary>Initializes the <see cref="Progress{T}"/> with the specified callback.</summary> 
    /// <param name="handler"> 
    /// A handler to invoke for each reported progress value. This handler will be invoked 
    /// in addition to any delegates registered with the <see cref="ProgressChanged"/> event. 
    /// Depending on the <see cref="System.Threading.SynchronizationContext"/> instance captured by 
    /// the <see cref="Progress"/> at construction, it's possible that this handler instance 
    /// could be invoked concurrently with itself. 
    /// </param> 
    /// <exception cref="System.ArgumentNullException">The <paramref name="handler"/> is null (Nothing in Visual Basic).</exception> 
    public SynchronousProgress(Action<T> handler) : this() 
    { 
     if (handler == null) throw new ArgumentNullException("handler"); 
     m_handler = handler; 
    } 

    /// <summary>Raised for each reported progress value.</summary> 
    /// <remarks> 
    /// Handlers registered with this event will be invoked on the 
    /// <see cref="System.Threading.SynchronizationContext"/> captured when the instance was constructed. 
    /// </remarks> 
    public event EventHandler<T> ProgressChanged; 

    /// <summary>Reports a progress change.</summary> 
    /// <param name="value">The value of the updated progress.</param> 
    protected virtual void OnReport(T value) 
    { 
     // If there's no handler, don't bother going through the [....] context. 
     // Inside the callback, we'll need to check again, in case 
     // an event handler is removed between now and then. 
     Action<T> handler = m_handler; 
     EventHandler<T> changedEvent = ProgressChanged; 
     if (handler != null || changedEvent != null) 
     { 
      // Post the processing to the [....] context. 
      // (If T is a value type, it will get boxed here.) 
      m_synchronizationContext.Send(m_invokeHandlers, value); 
     } 
    } 

    /// <summary>Reports a progress change.</summary> 
    /// <param name="value">The value of the updated progress.</param> 
    void IProgress<T>.Report(T value) { OnReport(value); } 

    /// <summary>Invokes the action and event callbacks.</summary> 
    /// <param name="state">The progress value.</param> 
    private void InvokeHandlers(object state) 
    { 
     T value = (T)state; 

     Action<T> handler = m_handler; 
     EventHandler<T> changedEvent = ProgressChanged; 

     if (handler != null) handler(value); 
     if (changedEvent != null) changedEvent(this, value); 
    } 
} 

/// <summary>Holds static values for <see cref="Progress{T}"/>.</summary> 
/// <remarks>This avoids one static instance per type T.</remarks> 
internal static class ProgressStatics 
{ 
    /// <summary>A default synchronization context that targets the ThreadPool.</summary> 
    internal static readonly SynchronizationContext DefaultContext = new SynchronizationContext(); 
} 
+0

Bu harika bir alternatif çözüm - bunu bilmiyordum! Güzel. –

İlgili konular