Форма безопасного ведения потока - PullRequest
1 голос
/ 23 июня 2011

У меня есть форма, ответственная за регистрацию всей необходимой информации из всех потоков в его richTextBox. Я использую Invoke, когда хочу получить доступ к его элементам управления из разных потоков (для добавления текста в richTextBox), и он отлично работает.

Но мне нужно, чтобы эта форма журнала отображалась, когда первое сообщение будет добавлено к richTextBox, и я не буду знать, какой из моих потоков сделает это первым. Также, когда я закрываю форму журнала, я хочу, чтобы она снова отображалась при поступлении следующего сообщения (и в этом случае я все еще не буду знать, какой поток будет вызывать его первым).

Я пытался создать эту форму в новых темах и через Application.Run (ApplicationContext), но ни одно из этих решений не сработало.

У вас есть какие-нибудь намеки?

Ответы [ 2 ]

3 голосов
/ 23 июня 2011

Вместо того, чтобы закрывать / повторно вызывать окна, часто проще просто скрыть и показать.

Создайте окно всякий раз, когда вы создаете главное окно, и никогда не закрывайте его.Добавьте обработчик события onbeforeclose (или как-то еще, что был вызван снова ...), который отменяет закрытие, инициированное пользователем, и вместо этого скрывает окно.

Теперь вы уже используете поточно-ориентированный диспетчер для изменениясодержимое окна: просто добавьте одну строку в этот обработчик событий, чтобы отобразить окно (это ничего не изменит, если оно уже видно), и все готово!

Кстати, полезная вещь вэти сценарии являются пользовательским подклассом TextWriter.Вы можете сделать это следующим образом:

public abstract class AbstractTextWriter : TextWriter {
    protected abstract void WriteString(string value);
    public override Encoding Encoding { get { return Encoding.Unicode; } }
    public override void Write(char[] buffer, int index, int count) {
        WriteString(new string(buffer, index, count));
    }
    public override void Write(char value) {
        WriteString(value.ToString(FormatProvider));
    }
    public override void Write(string value) { WriteString(value); }
    //subclasses might override Flush, Dispose and Encoding
}
public class DelegateTextWriter : AbstractTextWriter {
    readonly Action<string> OnWrite;
    readonly Action OnClose;
    static void NullOp() { }
    public DelegateTextWriter(Action<string> onWrite, Action onClose = null) { 
        OnWrite = onWrite; 
        OnClose = onClose ?? NullOp; 
    }
    protected override void WriteString(string value) { OnWrite(value); }
    protected override void Dispose(bool disposing) { 
        OnClose(); base.Dispose(disposing); 
    }
}

Таким образом, вы можете вставить свою логику обновления формы в нечто вроде этого ...

var threadSafeLogWriter =  new DelegateTextWriter(str => {
        Action updateCmd = ()=>{
            myControl...//append text to whatever control here
            myControl.Show();
        };
        if(myControl.InvokeRequired) myControl.BeginInvoke(updateCmd);
        else updateCmd();
    });

... и использовать ее повсюду,потенциально даже делает Console.SetOut, чтобы перехватить вывод до Console.Write.Можно написать Write this TextWriter в нескольких потоках, поскольку реализация самосинхронизируется.

Вы можете проверить это следующим образом ...

Console.SetOut(threadSafeLogWriter);
Parallel.For(0,100, i=>{
    threadSafeLogWriter.Write("Hello({0}) ", i);
    Thread.Yield();
    Console.WriteLine("World({0})!",i);
});

ЕслиВы делаете много регистрации маленьких сообщений, у вас может быть много вызовов BeginInvoke, и они медленные.В качестве оптимизации вы можете вместо этого поставить все сообщения журнала в ConcurrentQueue или в другую синхронизированную структуру, и только в BeginInvoke, если очередь была пуста до того, как вы в нее включились.Таким образом, вы обычно делаете только один BeginInvoke между обновлениями пользовательского интерфейса;и когда пользовательский интерфейс приступает к выполнению своих задач, он очищает флаг, а затем добавляет весь помещенный в очередь текст сразу.Однако, поскольку все потоки, ведущие журналирование, просто сбрасывают свои сообщения в том порядке, в каком они поступают в регистратор, для удобства чтения может быть очень плохо иметь множество небольших операторов журнала;Лучше всего сразу записать как можно большую строку, чтобы она не прерывалась сообщениями других потоков;и если вы сделаете это, то у вас не будет много BeginInvoke'а, так или иначе.будет меньше проблем.

1 голос
/ 23 июня 2011

Похоже на этот другой ответ Я поставлю сообщение в очередь и разрешу форме убрать их, так что порядок будет сохранен. Необходимо предусмотреть некоторую параллельную стратегию, чтобы позволить потокам подпитывать очередь, пока форма удаляет их из очереди, но вы гарантируете, что порядок событий сохранен, и вы можете запустить форму при первом увиденном событии.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...