【问题标题】:enumerate through a list or dict every x seconds with timer or dispatchertimer in C#在 C# 中使用 timer 或 dispatchertimer 每 x 秒通过列表或 dict 枚举
【发布时间】:2011-10-28 14:35:31
【问题描述】:

假设我有一个包含 3 个值/键对的字典。

private void someMethod()
{
    Dictionary<string, int> d = new Dictionary<string, int>();
    d.Add("cat", 22);
    d.Add("dog", 14);
    d.Add("llama", 2);
    d.Add("iguana", 6);

    somesortoftimercode
}

private void DisplayText(string x, int y)
{
    label1.Text = x;
    int someValue= 3+y;
}

我想要遍历这本字典,我想要一个调度器定时器(或定时器)每 3 秒调用一次带有字典值的 displayText。我该怎么做?

更新:

我不能使用 Thread.Sleep(XXX),我不能阻塞线程。我在后台还有其他东西,我不能把它转出来让线程遍布整个地方。

加: http://msmvps.com/blogs/peterritchie/archive/2007/04/26/thread-sleep-is-a-sign-of-a-poorly-designed-program.aspx

【问题讨论】:

  • 你使用的是什么版本的 C#?

标签: c# timer dispatchertimer


【解决方案1】:
private Timer timer;

private void someMethod()
{
    var d = new Dictionary<string, int>
                {
                    {"cat", 22}, 
                    {"dog", 14}, 
                    {"llama", 2}, 
                    {"iguana", 6}
                };

    int index = 0;
    TimerCallback timerCallBack = state =>
                                        {
                                            DisplayText(d.ElementAt(index).Key, d.ElementAt(index).Value);
                                            if(++index == d.Count)
                                            {
                                                index = 0;
                                            }
                                        };
    timer = new Timer(timerCallBack, null, TimeSpan.Zero, TimeSpan.FromSeconds(3));
}

private void DisplayText(string x, int y)
{
    label1.Text = x;
    int someValue= 3+y;
}

如果您只需要枚举字典一次,您可以使用以下代码:

new Task(() =>
    {
        d.All(kvp =>
        {
            DisplayText(kvp.Key, kvp.Value);
            Thread.Sleep(3000);
            return true;
        });
    }
).Start();

【讨论】:

  • 你可以阻塞另一个线程而不是主线程。我已经更新了答案。
【解决方案2】:

您可以使用框架提供的任何计时器,例如

System.Threading.Timers.Timer

将间隔设置为您想要的任何值,然后在 Tick 事件中调用 foreach 循环以遍历您的集合。根据您的示例

foreach(var pair in d)
{ 
   DisplayText(pair.key, pair.value);
}

【讨论】:

  • 您可以通过 foreach (var kvp in d) { DisplayText(kvp.Key, kvp.Value); } 节省几毫秒,以避免每次都查找值。
  • @JoeEnos 如果一个键有多个值,实际上会更正确,这会改变我的答案。
  • 不,这对我不起作用。 foreach 循环将一次遍历每一个,而不是在指定时间遍历一个。
【解决方案3】:

创建一个带有名为labell 的标签的表单并尝试以下代码:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        someMethod();
    }

    Thread thread;
    private void someMethod()
    {
        Dictionary<string, int> d = new Dictionary<string, int>();
        d.Add("cat", 22);
        d.Add("dog", 14);
        d.Add("llama", 2);
        d.Add("iguana", 6);

        thread = new Thread(new ParameterizedThreadStart(Do));
        thread.Start(d);
    }

    delegate void _DisplayText(string x, int y);
    private void DisplayText(string x, int y)
    {
        if (this.InvokeRequired)
        {
            this.Invoke(new _DisplayText(DisplayText), x, y);
            return;
        }
        label1.Text = x;
        int someValue = 3 + y;
    }

    public void Do(object dic)
    {
        Dictionary<string, int> d = (Dictionary<string, int>)dic;
        while (true)
        {
            foreach (var item in d)
            {
                DisplayText(item.Key, item.Value);
                Thread.Sleep(3000);
            }
        }
    }
}

【讨论】:

  • 不,这不能用 thread.sleep 完成。我不能让它在等待时阻塞
  • 请注意,它在等待时不会阻塞,因为我创建了另一个线程。
  • 如果你试试这个示例代码,你会发现你仍然可以与程序的 GUI 交互,而另一个线程导致标签每 3 秒改变一次。
【解决方案4】:

这应该会让你朝着正确的方向前进。我假设您正在创建一个 Win Forms 应用程序。如果没有,这对你不起作用。

    private System.Windows.Forms.Timer t = new System.Windows.Forms.Timer();
    private Dictionary<string, int> d = new Dictionary<string, int>()
    {
        {"cat", 22},
        {"dog", 14},
        {"llama", 2},
        {"iguana", 6}
    };
    public Form1()
    {
        InitializeComponent();
        t.Tick += new EventHandler(t_Tick);
        t.Start();
    }

    void t_Tick(object sender, EventArgs e)
    {
        t.Stop();
        foreach (var item in d)
        {
            label1.Text = item.Key;
            int someValue = 3 + item.Value;
        }
        t.Start();
    }

【讨论】:

    【解决方案5】:

    Reactive Extensions (Rx)(来自 Microsoft 云团队)有一种非常好的方式来做你想做的事。

    添加对 Rx 的引用(您可以通过 NuGet 完成)后,您只需将其放入您的 someMethod 方法中:

    Observable
        .Interval(TimeSpan.FromSeconds(3.0))
        .ObserveOnDispatcher()
        .Subscribe(x =>
        {
            d.ToArray() // Helps to prevent race conditions
                .ForEach(kvp => DisplayText(kvp.Key, kvp.Value));
        });
    

    它设置计时器 - 在后台线程上触发 - 然后将调用编组到调度程序(即 UI 线程)以防止跨线程问题。

    如果您使用的是 Windows 窗体,那么 .ObserveOnDispatcher() 将变为 .ObserveOn(label1).ObserveOn(form),您就可以开始了。

    无需为显式创建线程、计时器或后台工作人员而烦恼。

    这里是 Rx 的链接:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-09-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多