Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/294.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# C语言中的基本多线程问题#_C#_Winforms_Multithreading - Fatal编程技术网

C# C语言中的基本多线程问题#

C# C语言中的基本多线程问题#,c#,winforms,multithreading,C#,Winforms,Multithreading,我尝试使用几个线程来实现以下目标: List<Thread> threads = new List<Thread>(); for (int i = 0; i < 5; i++) threads.Add(new Thread(CheckTradeOneForOne)); foreach (Thread t in threads) t.Start(); progr

我尝试使用几个线程来实现以下目标:

 List<Thread> threads = new List<Thread>();
        for (int i = 0; i < 5; i++)
            threads.Add(new Thread(CheckTradeOneForOne));

        foreach (Thread t in threads)
            t.Start();
            progressBarTrades.Value = 0;
        count = 0;
  > Starting task for source 1 
  > Starting task for source 2 
  > Starting task for source 3 
  < Finished task for source 3 
  > Starting task for source 4 
UI notified that task for source 3 finished; overall 1 tasks finished 
  < Finished task for source 2 
  > Starting task for source 5 
UI notified that task for source 2 finished; overall 2 tasks finished 
  < Finished task for source 1 
  > Starting task for source 6 
UI notified that task for source 1 finished; overall 3 tasks finished 
  < Finished task for source 4 
  > Starting task for source 7 
UI notified that task for source 4 finished; overall 4 tasks finished 
  < Finished task for source 5 
  > Starting task for source 8 
UI notified that task for source 5 finished; overall 5 tasks finished 
  < Finished task for source 6 
  > Starting task for source 9 
UI notified that task for source 6 finished; overall 6 tasks finished 
  < Finished task for source 8 
  > Starting task for source 10 
UI notified that task for source 8 finished; overall 7 tasks finished 
  < Finished task for source 7 
  > Starting task for source 11 
UI notified that task for source 7 finished; overall 8 tasks finished 
  < Finished task for source 9 
  > Starting task for source 12 
UI notified that task for source 9 finished; overall 9 tasks finished 
  < Finished task for source 10 
  < Finished task for source 11 
  > Starting task for source 13 
UI notified that task for source 10 finished; overall 10 tasks finished 
UI notified that task for source 11 finished; overall 11 tasks finished 
  > Starting task for source 14 
  < Finished task for source 14 
  > Starting task for source 15 
UI notified that task for source 14 finished; overall 12 tasks finished 
  < Finished task for source 13 
  > Starting task for source 16 
UI notified that task for source 13 finished; overall 13 tasks finished 
  < Finished task for source 12 
  > Starting task for source 17 
UI notified that task for source 12 finished; overall 14 tasks finished 
  < Finished task for source 16 
  > Starting task for source 18 
UI notified that task for source 16 finished; overall 15 tasks finished 
  < Finished task for source 15 
UI notified that task for source 15 finished; overall 16 tasks finished 
  > Starting task for source 19 
  < Finished task for source 17 
UI notified that task for source 17 finished; overall 17 tasks finished 
  < Finished task for source 18 
  > Starting task for source 20 
UI notified that task for source 18 finished; overall 18 tasks finished 
  < Finished task for source 19 
UI notified that task for source 19 finished; overall 19 tasks finished 
  < Finished task for source 20 
UI notified that task for source 20 finished; overall 20 tasks finished 
List threads=newlist();
对于(int i=0;i<5;i++)
添加(新线程(选中TradeOneForOne));
foreach(螺纹中的螺纹t)
t、 Start();
progressBarTrades.Value=0;
计数=0;
而count是在类级别定义的,progressbar是progressbar(winforms^ ^)

private void CheckTradeOneForOne()
{
电流;
while(count
交易为列表(长度约为1000)

我想通过所有交易,每个交易做一个httpwebrequest, 因此我想使用多线程, 问题是,尽管我使用了lock,但另一个线程使用buttonaction\progressbar的第二个锁出现了错误

还有一件事,这是使用多线程的正确方法吗? 理想使用的线程数是多少? 锁是否确保没有移动,然后一个线程采取相同的贸易字符串


tyvm供您帮助:)

使用windows窗体使用进度条和线程的最佳方式是在MSDN中,这是一个很好的例子。

您不能从非窗体线程的线程访问windows窗体或wpf控件。 为此,应使用BeginInvoke或类SynchronizationContext

然而,由于您只是更新一个progressbar,我建议您使用一个简单的计时器,如果您有很多线程,它会更快

相反,我还建议您使用ThreadPool,这将避免实例化太多的线程

作为一个例子,我发布了一些代码

private volatile int TradesCount;
private List<...> Trades;

void MyFunction()
{
    List<Thread> threads = new List<Thread>();
    for (int i = 0; i < 5; i++)
        threads.Add(new Thread(CheckTradeOneForOne));

    timer1.Enabled = true;

    progressBarTrades.Value = 0;
    this.TradesCount = 0;

    foreach (Thread t in threads)
        t.Start();
}

void timer1_Tick(object sender, EventArgs e)
{
    int count = this.TradesCount;
    if (count >= Trades.Count)
    {
        count = Trades.Count;
        timer1.Enabled = false;
    }

    progressBarTrades.Value = (100 * count) / Trades.Count;
    buttonAction.Text = count.ToString();
}

private void CheckTradeOneForOne()
{
    int current;
    for (;;)
    {
        // This will give you a warning, but you can ignore it with a #pragma, it is allowed to use Interlocked.Increment and volatile fields.
        current = Interlocked.Increment(ref TradesCount) - 1;

        if (current >= Trades.Count)
            break; // We can exit the loop.

        temppage = HelpClass.GetSourceCodeForTrade(Trades[current], sessid, profilenum);

        //if the trainer has requested anything?
        if (!HelpClass.RequestAnything(temppage))
        {
            ...
        }
    }
}
private volatile int tradescont;
私人上市行业;
void MyFunction()
{
列表线程=新列表();
对于(int i=0;i<5;i++)
添加(新线程(选中TradeOneForOne));
timer1.Enabled=true;
progressBarTrades.Value=0;
此.TradesCount=0;
foreach(螺纹中的螺纹t)
t、 Start();
}
无效计时器1_刻度(对象发送方,事件参数e)
{
int count=this.tradescont;
如果(计数>=Trades.count)
{
计数=交易。计数;
timer1.Enabled=false;
}
progressBarTrades.Value=(100*count)/Trades.count;
buttonAction.Text=count.ToString();
}
私有无效支票TradeOneForOne()
{
电流;
对于(;;)
{
//这将给您一个警告,但您可以使用#pragma忽略它,允许使用Interlocked.Increment和volatile字段。
电流=联锁。增量(参考TradesCount)-1;
如果(当前>=Trades.Count)
break;//我们可以退出循环。
temppage=HelpClass.GetSourceCodeForTrade(交易[当前]、会话、profilenum);
//培训师是否有任何要求?
如果(!HelpClass.RequestAnything(temppage))
{
...
}
}
}
我使用的是联锁。增量而不是锁定,速度更快。
看看google上的Interlocated.Increment,它是一个非常好的函数:原子增量。

我同意其他答案,建议使用.NET线程池,而不是自己启动新线程。如果您有幸使用.NET 4或更高版本,我会更进一步,建议您使用(即使您目前没有使用.NET 4,在.NET线程池之上构建面向任务的抽象也非常简单)

使用任务IMHO的最大优点之一是它们是即将到来的C 5代码的基础,等待< /COD>内置语言特征。因此,如果您今天使用任务进行后台处理,那么您已经在为将来准备程序:-)

为了展示如何使用任务来解决眼前的问题,我编写了一个简单的WinForms程序,该程序演示了进行后台处理和更新进度条以跟踪已完成多少任务的技术:

// file "Program.cs"
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ProgressDialog
{
    static class Program
    {
        [STAThread] static void Main()
        {
            // build UI
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            var rootForm = new Form { Text = @"Test Form", Width = 300, Height = 100 };
            var btn = new Button { Text = @"Start", Parent = rootForm, Dock = DockStyle.Top };
            var progress = new ProgressBar { Minimum = 0, Parent = rootForm, Dock = DockStyle.Top, Style = ProgressBarStyle.Continuous };
            new Label { Text = @"Progress:", Parent = rootForm, Dock = DockStyle.Top, AutoSize = true };


            // define parameters
            const int sourcesCount = 20; // how many sources do we want to process
            var completedCount = 0;
            var randomGenerator = new Random();
            var timer = new Stopwatch();


            // callback that will be invoked on UI thread each time a task finishes
            Action<int> onTaskCompleted = source =>
            {
                ++completedCount; // we're modifying "completedCount" closure on purpose
                progress.Value = completedCount;
                System.Diagnostics.Debugger.Log(0, null, string.Concat(@"UI notified that task for source ", source, @" finished; overall ", completedCount, @" tasks finished", Environment.NewLine));
                if (completedCount == sourcesCount)
                {
                    timer.Stop();
                    btn.Enabled = true;
                    btn.Text = string.Concat(@"Finished (took ", timer.ElapsedMilliseconds, @" milliseconds). Start again");
                }
            };


            // task itself (the hard part :) )
            Action<int> task = source =>
            {
                System.Diagnostics.Debugger.Log(0, null, string.Concat(@"  > Starting task for source ", source, Environment.NewLine));
                Thread.Sleep(randomGenerator.Next(100, 200)); // simulate some workload (taking between 100 and 200 milliseconds)
                System.Diagnostics.Debugger.Log(0, null, string.Concat(@"  < Finished task for source ", source, Environment.NewLine));
                rootForm.BeginInvoke(new Action(() => onTaskCompleted(source)));
            };


            // start button handler (kick-starts the background tasks)
            btn.Click += (src, args) =>
            {
                btn.Enabled = false;
                btn.Text = @"Running...";
                progress.Maximum = sourcesCount;
                progress.Value = 0;
                timer.Restart();

                completedCount = 0;
                var sources = Enumerable.Range(1, sourcesCount); // simulate getting data for each task
                var tasks = sources
                    .Select(s => Task.Factory.StartNew(() => task(s))) // at this point we only have an enumerable that is able to start all the tasks, nothing is running yet
                    .ToArray(); // now the tasks are started

                if (tasks.Length != sourcesCount) { throw new InvalidOperationException(); } // assert that we created one task for each source
            };


            // show the form now, let the user interact with it
            Application.Run(rootForm);
        }
    }
}

正如一些人已经提到的,然后使用
ThreadPool
,而不是自己创建线程。通过使用
ThreadPool
,您不必担心要生成的线程数量,
ThreadPool
将为您做到这一点

假设您使用的是.NET 4,您可以利用TPL:

public partial class Form1 : Form
{
    private volatile int count;
    private readonly int total;

    public Form1()
    {
        InitializeComponent();

        var urls = new List<string> { "http://something.com", "http://another.com" };
        total = urls.Count;

        // Execute the Parallel loop in a thread from the threadpool, 
        // in order not to block the UI thread.
        ThreadPool.QueueUserWorkItem(o =>
        {
            Parallel.ForEach(urls, x => MakeRequest(x));
        });

        // other UI stuff here?
    }

    public void MakeRequest(string url)
    {
        // code for web request here...

        int newCount = Interlocked.Increment(ref count);
        Invoke(new Action(() => progressBar.Value = (100 * newCount) / total));
    }
}
公共部分类表单1:表单
{
私有可变整数计数;
私有只读整型;
公共表格1()
{
初始化组件();
var url=新列表{”http://something.com", "http://another.com" };
总数=URL.Count;
//在线程池中的线程中执行并行循环,
//为了不阻塞UI线程。
ThreadPool.QueueUserWorkItem(o=>
{
ForEach(url,x=>MakeRequest(x));
});
//这里还有其他用户界面吗?
}
公共void生成请求(字符串url)
{
//这里是web请求的代码。。。
int newCount=联锁增量(参考计数);
调用(新操作(()=>progressBar.Value=(100*newCount)/total));
}
}

在谷歌搜索Interlocated.increment后,使用Threadpool(属于主题,但强烈建议使用)tyvm获得非常详细的评论。我发现它比lock快得多。但我留下了两个问题:1)SynchronizationContext。目前,我每次使用多线程时都会使用它吗?2) 我怎么知道多少线程会给我最好的性能?tyvm:)SynchronizationContext是一个类,允许您将函数作为“消息”发布
public partial class Form1 : Form
{
    private volatile int count;
    private readonly int total;

    public Form1()
    {
        InitializeComponent();

        var urls = new List<string> { "http://something.com", "http://another.com" };
        total = urls.Count;

        // Execute the Parallel loop in a thread from the threadpool, 
        // in order not to block the UI thread.
        ThreadPool.QueueUserWorkItem(o =>
        {
            Parallel.ForEach(urls, x => MakeRequest(x));
        });

        // other UI stuff here?
    }

    public void MakeRequest(string url)
    {
        // code for web request here...

        int newCount = Interlocked.Increment(ref count);
        Invoke(new Action(() => progressBar.Value = (100 * newCount) / total));
    }
}