使用线程和.Invoke()和控件仍然保持不活动状态 - C#

时间:2011-02-06 21:05:14

标签: c# multithreading invoke

我正在尝试用一些数据填充文本框,即一次一行一行的几个乐器的名称。

我有一个将生成并返回一个乐器列表的类,然后我遍历列表并在每次迭代后在文本框中添加一个新行。

启动帖子:

private void buttonListInstruments_Click(object sender, EventArgs e)
        {
            if (ins == null)
            {
                ins = new Thread(GetListOfInstruments);
                ins.Start();
            }
            else if (ins != null)
            {
                textBoxLog.AppendText("Instruments still updating..");
            }

        }

委派更新文本框:

public delegate void UpdateLogWithInstrumentsCallback(List<Instrument> instruments);

private void UpdateInstruments(List<Instrument> instruments)
        {
            textBoxLog.AppendText("Listing available Instruments...\n");

            foreach (var value in instruments)
            {
                textBoxLog.AppendText(value.ToString() + "\n");
            }
            textBoxLog.AppendText("End of list. \n");

            ins = null;
        }

调用控件:

private void GetListOfInstruments()
        {
            textBoxLog.Invoke(new UpdateLogWithInstrumentsCallback(this.UpdateInstruments),
                new object[] { midiInstance.GetInstruments() });
        }

注意:GetInstruments()返回工具类型的列表

我正在实现therads以尝试在文本框更新时保持GUI功能。 出于某种原因,WinForm上的其他UI控件(如单独的组合框)在按下时保持非活动状态,直到文本框完成更新。

我是否正确使用线程?

感谢。

6 个答案:

答案 0 :(得分:3)

你还没有完成任何事情,UpdateInstruments()方法仍然在UI线程上运行,就像之前一样。不太确定为什么你会看到如此长的延迟,这必须是大量的工具。通过首先将所有这些都附加到StringBuilder中,然后将其ToString()值附加到TextBox,可以减慢它的速度。这削减了相当昂贵的Windows电话。

答案 1 :(得分:1)

我建议一般使用SynchronizationContext

从UI线程,例如初始化:

// make sure a SC is created automatically
Forms.WindowsFormsSynchronizationContext.AutoInstall = true;
// a control needs to exist prior to getting the SC for WinForms
// (any control will do)
var syncControl = new Forms.Control();
syncControl.CreateControl();
SyncrhonizationContext winformsContext = System.Threading.SynchronizationContext.Current;

稍后,从任何希望发布到上述SC的主题:

// later on -- no need to worry about Invoke/BeginInvoke! Whoo!
// Post will run async and will guarantee a post to the UI message queue
// that is, this returns immediately
// it is OKAY to call this from the UI thread or a non-UI thread
winformsContext.Post(((state) => ..., someState);

正如其他人所指出的那样,要么让UI更新动作更快(这是更好的方法!!! ),要么将其分成多个动作发布到UI队列(如果您发布到队列中,则队列中的其他消息将不会被阻止)。以下是将操作“分块”到一小段时间直到完成所有操作的示例 - 它假定在收集数据后调用UpdateStuff,并且当集合本身花费显着时间时不一定适合。这并没有考虑到“停止”并且因为它使用闭包而不是传递状态而有点混乱。无论如何,享受。

void UpdateStuff (List<string> _stuff) {
    var stuff = new Queue<string>(_stuff); // make copy
    SendOrPostCallback fn = null; // silly so we can access in closure
    fn = (_state) => {
        // this is in UI thread
        Stopwatch s = new Stopwatch();
        s.Start();
        while (s.ElapsedMilliseconds < 20 && stuff.Count > 0) {
          var item = stuff.Dequeue();
          // do stuff with item
        }
        if (stuff.Count > 0) {
          // have more stuff. we may have run out of our "time-slice"
          winformsContext.Post(fn, null);
        }
    };
    winformsContext.Post(fn, null);
}

快乐的编码。

答案 2 :(得分:0)

更改此行:

textBoxLog.Invoke(new UpdateLogWithInstrumentsCallback(this.UpdateInstruments),
                new object[] { midiInstance.GetInstruments() });

用这个:

textBoxLog.BeginInvoke(new UpdateLogWithInstrumentsCallback(this.UpdateInstruments),
                new object[] { midiInstance.GetInstruments() });

答案 3 :(得分:0)

您正在将所有乐器一次性送入文本框,而不是逐个进入线程。对Invoke的调用应将放在 for-loop中,而不是将其包围。

答案 4 :(得分:0)

nope,你启动一个线程,然后使用invoke,这基本上意味着你要回到UI线程来完成工作......所以你的线程什么都不做!

答案 5 :(得分:0)

您可能会发现首先构建字符串并在一个块中附加到文本框而不是逐行附加更有效。然后,字符串连接操作也可以在辅助线程上完成。