【问题标题】:c# PLinq AsParallel Select hangsc# Plinq AsParallel Select 挂起
【发布时间】:2016-11-23 13:33:38
【问题描述】:

我想处理存储在文本文件中的大量数据。这是我用来使其工作更快的代码:

var result = File
   .ReadLines(textBox1.Text)
   .AsParallel()
   .WithDegreeOfParallelism(100)
   .Select(line => ProcessLine(line));

ProcessLine 方法获取该行,然后对其进行处理并将其添加到 ArrayList

所有处理完成后,我将ArrayList 加载到数据网格中, 但有时它会完成所有行,有时它会挂起,我不知道为什么。

有什么建议吗?

更新 这是方法 ProcessLine

private string  ProcessLine(string domain)
        {



            ProcessStartInfo cmdinfo = new ProcessStartInfo();
            cmdinfo.FileName = "cmd.exe";
            cmdinfo.Arguments = "/c nslookup";
            cmdinfo.RedirectStandardInput = true;
            cmdinfo.RedirectStandardOutput = true;
            cmdinfo.CreateNoWindow = true;
            cmdinfo.UseShellExecute = false;
            cmdinfo.RedirectStandardError = false;
            Process cmdd = new Process();
            cmdd = Process.Start(cmdinfo);
            string spf = "none";
        createproc:
            try
            {



                cmdd.StandardInput.WriteLine("set q=txt");
                cmdd.StandardInput.Flush();
                cmdd.StandardInput.WriteLine(domain);

                cmdd.StandardInput.WriteLine("exit");
                cmdd.StandardInput.WriteLine("exit");
                StreamReader r = cmdd.StandardOutput;

                //cmdd.WaitForExit();
                cmdd.Close();
                spf = "";
                string rdl = string.Empty;
                bool spffound = false;
                while (rdl != null)
                {
                    try
                    {

                        rdl = r.ReadLine();

                        if (rdl.Contains("v=spf"))
                        {
                            spffound = true;
                            spf = rdl.Trim();
                            this.Invoke(new MethodInvoker(delegate
                            {
                                textBox2.AppendText("domain found : " + domain + Environment.NewLine + "SPF = " + spf + Environment.NewLine);
                                textBox2.Update();

                            }));
                            break;
                        }

                    }
                    catch (Exception)
                    {

                    }
                }
                if (!spffound)
                    spf = "none";

                nbrDoms++;
                this.Invoke(new MethodInvoker(delegate
                {
                    DomsElapsed.Text = nbrDoms + " Domains Elapsed";
                    DomsElapsed.Update();

                }));
                SPFRecord srx = new SPFRecord((string)spf.Clone(), (string)domain.Clone());

                if (srx == null)
                {
                    cmdd.Kill();
                    cmdinfo = new ProcessStartInfo();
                    cmdinfo.FileName = "cmd.exe";
                    cmdinfo.Arguments = "/c nslookup";
                    cmdinfo.RedirectStandardInput = true;
                    cmdinfo.RedirectStandardOutput = true;
                    cmdinfo.CreateNoWindow = true;
                    cmdinfo.UseShellExecute = false;
                    cmdinfo.RedirectStandardError = false;

                    cmdd = new Process();
                    cmdd.StartInfo = cmdinfo;
                    cmdd.Start();

                    goto createproc;
                }

                lock (pageManager)
                {
                    pageManager.AddRecord(srx);
                }
                //this.Invoke(new MethodInvoker(delegate
                //{
                //}));

            }
            catch(Exception exc)
            {
                cmd.Kill();

                cmdinfo = new ProcessStartInfo();
                cmdinfo.FileName = "cmd.exe";
                cmdinfo.Arguments = "/c nslookup";
                cmdinfo.RedirectStandardInput = true;
                cmdinfo.RedirectStandardOutput = true;
                cmdinfo.CreateNoWindow = true;
                cmdinfo.UseShellExecute = false;
                cmdinfo.RedirectStandardError = false;

                cmdd = new Process();
                cmdd.StartInfo = cmdinfo;
                cmdd.Start();
                Thread.Sleep(10);
                goto createproc;
            }
            return "";
        }

【问题讨论】:

  • 你能把代码贴在ProcessLine方法里吗?我怀疑那里有比赛条件。
  • @RePierre 看看更新
  • 你能删除WithDegreeOfParallelism(100)吗,这是奇怪的并行并行,让它默认工作
  • 当我使用 WithDegreeOfParallelism(100) 时效果很好,有时当我使用 WithDegreeOfParallelism(1) 时效果很好,但我需要速度:(
  • 是的,删除 WithDegreeOfParallelism 仍然无法正常工作

标签: c# multithreading linq parallel-processing


【解决方案1】:

将文本行读入字符串,例如 file.readalllines(psudo code)

基本上每个线程都在锁定另一个线程,您是为了提高速度还是因为文件太大而无法放入内存?

【讨论】:

  • 你是怎么猜到线程互相锁定的,原来的问题中没有可用的
  • 我正在尝试这个以提高速度,因为我有 +15000000 条记录,我处理每个记录,然后将其添加到页面管理器,如果没有页面,页面管理器会创建一个空页面 (ArrayList),然后它会做广告记录。如果页数 == 27 pagemanager 创建另一个页面,然后在流程结束后继续添加记录当用户单击下一步时,我将每个页面加载到数据网格上,它从所选页面加载并显示在数据网格上
  • Mrinal Kamboj - 我在阅读代码后假设锁定。我询问加载到内存的原因是有多个线程都试图从文本文件中读取,这是浪费时间。我会在一个字符串中阅读所有内容,然后线程。
【解决方案2】:

好的,有几点要提:

  1. 不要使用 goto 语句 - 很难理解你的方法做了什么。只需将Process 的创建移动到一个单独的方法中并调用该方法而不是使用goto
  2. 进程确实需要时间来加载,而且对于您想要做的事情来说相当多。为了避免这种负载损失,不要创建和调用进程,而是尝试用执行相同操作的方法替换它。有一个examplenslookup,而无需调用该进程。尝试根据您的需要进行调整
  3. 删除 locks - 如果您的应用程序以某种方式使用 100 个线程,那么 lock 就是浪费时间。您将有 99 个线程等待其他单个线程将其数据推送到pageManager。正如@Mrinal Kamboj 指出的那样,您可以使用线程安全的集合。在这种情况下,使用BlockingCollection<T> 并将结果添加到那里。在队列的另一端,pageManager 会在每个项目到达时进行监听和消费。
  4. UI 需要单独的周期来刷新,这也需要时间。如果pageManager.AddRecord() 必须以某种方式刷新 UI,那么其他线程不会只等待添加操作。
  5. UI 更新必须在创建控件的线程中完成,并且该线程如果它正在等待另一个线程,则无法更新 UI

整体算法应该是这样的:

public class Engine
{
    private readonly BlockingCollection<string> _messagePipeline = new BlockingCollection<string>();

    public BlockingCollection<string> MessagePipeline
    {
        get { return _messagePipeline; }
    }

    public void Process(string file)
    {
        File.ReadLines(file)
            .AsParallel()
            .ForAll(line =>
            {
                var nsLookupResult = NsLookupMethod(line);
                if(nsLookupResult.HasInfoYouNeed)
                    _messagePipeline.Add(nsLookupResult.DisplayInfo);
            });
    }
}

public class MainForm : Form
{
    private readonly Engine _engine; // ...

    private void OnStartButtonClick(object sender, EventArgs e)
    {
        var cts = new CancellationTokenSource();
        _engine.Process(textbox1.Text);
        Task.Factory.StartNew(()=>
        {
            foreach(var message in _engine.MessagePipeline.GetConsumingEnumerable())
            {
                // show the message
                Application.DoEvents(); // allow the app to process other events not just pushing messages.
            }
        }, cts.Token,
        TaskCreationOptions.PreferFairness,
        // Specify that you want UI updates to be done on the UI thread
        // and not on any other thread
        TaskScheduler.FromCurrentSynchronizationContext());
    }
}

应该这样做。我确实有(或多或少的学术)example 这种逻辑在起作用。 UI更新逻辑在应用程序的MainForm中,处理逻辑在Engine class中;看看那里。

【讨论】:

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