背景介绍:
     
     开发环境 :VS2010简体中文旗舰版
     .net 版本:.net4.0
     CPU      :双核四线程【Intel】
     错误现象 :使用 TPL 编写多核并行代码,发现只并行了其中的一部分(不能并行到底),之后就由一条线程执行到结束。编译时,代码没有问题,运行时,有时候出现运行到一定程度,程序就卡住,不再执行下去,就像死机一样。下面就贴出代码。
    C#并行TPL执行不彻底卡住

解决方案 »

  1.   

    首先贴出控制台执行程序,验证该代码是正确的。代码具体如下:
    using System;
    using System.Linq;
    using System.Text;
    using System.Collections.Generic;using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Forms;namespace Test
    {
        class A
        {
            public String strName;
            
            public A( String str )
            {
                this.strName = str;       
            }        public void printMessage()
            {
                System.DateTime dt = System.DateTime.Now;            for( int i = 0; i < 50000; i++ )
                {
                    Console.WriteLine( "当前时间 " + String.Format( "{0:yyyy-MM-dd HH::mm::ss::ffff}",dt ) + "   " + 
                                       " i = "   + i + "   " + this.strName );  // 注意时间的格式化;
                }           
            }
        }    class Program
        {
            static void Main( string[] args )
            {
                A a1 = new A( "11111" );
                A a2 = new A( "22222" );
                A a3 = new A( "33333" );
                A a4 = new A( "44444" );
                           System.Threading.ThreadStart del = delegate()
                {
                    try
                    {
                        System.Threading.Tasks.Parallel.Invoke( 
                                                                 () => a1.printMessage(),
                                                                 () => a2.printMessage(),
                                                                 () => a3.printMessage(),
                                                                 () => a4.printMessage()
                                                              );
                    }
                    catch( AggregateException ex )
                    {
                        System.Windows.Forms.MessageBox.Show( "出错了!" + "出错原因:" + ex.Message );               
                    }                
                };            System.Threading.Thread th = new System.Threading.Thread( del );
                th.IsBackground            = true;
                th.Start();            Console.WriteLine( "单击任意按键可结束测试!" );
                Console.ReadKey();
            }
        }
    }贴出图片,可以看出并行代码执行OK,且能执行到底。
      

  2.   

    上面的代码说明了 C# 的并行是没有问题的,下面我稍微修改了一下实现,即:将打印的位置由控制台转到 RichTextBox 中,现封装一个 C# DLL以具体实现并行代码,之后,创建一个应用窗体程序,由该程序去调用。C#  DLL:using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;//using System.Core;
    using System.Data;
    //using System.Data.DataSetExtensions;
    using System.Xml;
    using System.Xml.Linq;using System.Threading;
    using System.Windows.Forms;
    using System.Threading.Tasks;
    using System.Runtime.InteropServices;namespace SyncThread
    {
        public class SyncMethod
        {
            public System.Windows.Forms.RichTextBox txt;  // 指定要输出文本的文本框;
           static System.Threading.Semaphore sep = new System.Threading.Semaphore(10, 100);
            public SyncMethod( System.Windows.Forms.RichTextBox txt )
            {
                this.txt = txt;
            }        public void printMessage( String str,int index )
            {
                System.DateTime dt = System.DateTime.Now;            if( null == this.txt )
                {
                    MessageBox.Show( " txt 值为空!" );
                    return;
                }            for( int i = 0; i < 5000; i++ )
                {
                   this.txt.AppendText( "当前时间 " + String.Format("{0:yyyy-MM-dd HH::mm::ss}", dt) + "   " + str +
                                         " = " + i + "   "  + index + "\n" );   // 注意时间的格式化;
                                  sep.Release();    // 释放自增减锁;
                }          
            }
        };    public class DoWork
        {
            private SyncMethod st1;
            private SyncMethod st2;
            private SyncMethod st3;
            private SyncMethod st4;        public System.Windows.Forms.RichTextBox txt;        public DoWork( System.Windows.Forms.RichTextBox txt ) 
            {
                this.txt = txt;
                this.st1 = new SyncMethod( this.txt );
                this.st2 = new SyncMethod( this.txt );
                this.st3 = new SyncMethod( this.txt );
                this.st4 = new SyncMethod( this.txt );
            }        public void handle()
            {
                System.Threading.ThreadStart del = delegate()
                {
                    try
                    {
                        System.Threading.Tasks.Parallel.Invoke(
                                                                 () => this.st1.printMessage( "aaaa",1111 ),
                                                                 () => this.st2.printMessage( "bbbb",2222 ),
                                                                 () => this.st3.printMessage( "cccc",3333 ),
                                                                 () => this.st4.printMessage( "dddd",4444 )
                                                              );
                    }
                    catch (System.AggregateException)
                    {
                    }
                    catch (System.Exception)
                    {
                        // System.Windows.Forms.MessageBox.Show("出现其他错误," + "出错原因:" + e.Message);
                    }
                };            System.Threading.Thread th = new System.Threading.Thread( del );
                th.IsBackground            = true;
                th.Name                    = "dowork";
                th.Start();
            }        public void doParelWork() 
            {
                try
                {
                    System.Threading.Tasks.Task t1 = new System.Threading.Tasks.Task(() => this.st1.printMessage("aaaa", 1111));
                    System.Threading.Tasks.Task t2 = new System.Threading.Tasks.Task(() => this.st2.printMessage("bbbb", 2222));
                    System.Threading.Tasks.Task t3 = new System.Threading.Tasks.Task(() => this.st3.printMessage("cccc", 3333));
                    System.Threading.Tasks.Task t4 = new System.Threading.Tasks.Task(() => this.st4.printMessage("dddd", 4444));                t1.Start();
                    t2.Start();
                    t3.Start();
                    t4.Start();                System.Threading.Tasks.Task.WaitAll( t1, t2, t3, t4 );                
                }
                catch (System.AggregateException ex)
                {
                }
                catch (System.Exception e)
                {                
                }            
            }
        }
    }
      

  3.   

    以下将展示窗体调用部的代码,窗体中新建了一个按钮,使用按钮的 Click 事件执行并行程序,代码如下:
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;using SyncThread;namespace TestThread
    {
        public partial class Form1 : Form
        {
            private SyncThread.DoWork dw;   // 声明类;
            private ShowInfo sf;            // 声明委托;
            //
    // 委托;
            //
            private delegate void ShowInfo( System.Windows.Forms.RichTextBox txt );
            private void showInfo( System.Windows.Forms.RichTextBox txt ) 
            {
                this.dw.txt = txt;
            }        private void controlShow() 
            {
                if( this.InvokeRequired )   // 返回值为 true,表示来自 UI 线程;
                //if ( true )               // 返回值为 true,表示来自 UI 线程;
            {
                    Object[] value = { this.txtShow };
                    this.Invoke(this.sf, value);
            }         this.dw.handle();
                //this.dw.doParelWork();
            }        public Form1()
            {
                InitializeComponent();
            }        private void Form1_Load(object sender, EventArgs e)
            {
                this.dw = new SyncThread.DoWork( this.txtShow );  // 实例化类;
                this.sf = new ShowInfo(this.showInfo);            // 实例化;
            }        private void cmdStart_Click( object sender, EventArgs e )
            {
                System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;   // 这句话也可以加在这里,这么搞;
                controlShow();
            }
        }
    }执行结果描述如下:
    1、执行时,没有一次并行到底,总是:并行了其中一部分(不能并行到底),之后由一条线程执行到结束;
    2、有时候程序中途卡住,就像死机了一样,不响应系统的任何操作,但是系统的其他程序可以正常运行;
    3、有时候报告错误。
      

  4.   

    当然了,在非UI线程操作界面需要使用Control.Invoke让界面去刷新。
      

  5.   

    把你的 this.Invoke 改为 this.BeginInvoke。我印象中,我好像从来没有使用过 Invoke,都是 BeginIncoke。我从来都非常注意不要稀里糊涂地写出阻塞式的程序。
      

  6.   

    看得出你不会使用System.Threading.Semaphore,最简单的方法就是把它去掉。
      

  7.   

    另外,你在  catch (System.AggregateException ex)那个地方设置断点你就知道什么错误了,是Semaphore的问题。
      

  8.   

    感谢以上各位的热情回复,我突然发现我写错了一句代码:
     static System.Threading.Semaphore sep = new System.Threading.Semaphore(10, 100);因为我只启动四条线程,所以这句代码应该是:
     static System.Threading.Semaphore sep = new System.Threading.Semaphore(1, 4);现在可以并行到底了,但是 CPU de 的使用率  只有 40% ,并不高,以下贴图佐证:
    并行贴图展示:
    再问: 
      

  9.   

    刚才点错了,现在贴并行的图片:
    再次提问:1、为什么 CPU 的使用率只有 40% ,而控制台的使用了可以到达 95%?2、如何提高 CPU 的使用率? 求指点,TKS
      

  10.   

    你就把Semaphore去掉好拉。
    你把System.Threading.Semaphore(10, 100);改为System.Threading.Semaphore(1, 4);还是勿用Semaphore。
    我就不相信你那个sep.Release(); 4次循环后不会报错。但或许你看不到,你因为你吞掉了exception。
      

  11.   

    我尝试着放弃了 Semaphore ,即把:static System.Threading.Semaphore sep = new System.Threading.Semaphore(1, 4);
    sep.WaitOne();
    sep.Release(); 注释掉,结果发现,执行到了几百次就卡住了,程序无法执行下去,就像这个程序死了似的,贴图佐证:
      

  12.   

    哎,把 static System.Threading.Semaphore sep = new System.Threading.Semaphore(1, 4);
    跟sep.Release();  那两行注释掉就那么难吗?
    实在搞不懂你这里用Semaphore来做什么,如果你真的控制并发度应该由Parallel.Invoke ParallelOptions来控制啊。
    还有,你的程序问题很多,很多代码写的很随意的, 下面的代码要它干嘛的?根本起不到作用。
        if( this.InvokeRequired )   // 返回值为 true,表示来自 UI 线程;
                //if ( true )               // 返回值为 true,表示来自 UI 线程;
            {
                    Object[] value = { this.txtShow };
                    this.Invoke(this.sf, value);
            }另外,我就好奇为什么你能在main thread之外更新线程了,原来你用了CheckForIllegalCrossThreadCalls,这????
      

  13.   

    【 SQL_Beginner Debugging之傲气魔君】:   你好,那两行我已经注释了的(见 11 楼 本人回复),你说的:  if( this.InvokeRequired )   // 返回值为 true,表示来自 UI 线程;
                //if ( true )               // 返回值为 true,表示来自 UI 线程;
            {
                    Object[] value = { this.txtShow };
                    this.Invoke(this.sf, value);
            }
    根本不起作用,是真的,感谢赐教。使用:CheckForIllegalCrossThreadCalls 是因为我是用线程启动并行程序的,这种方式可行,因为控制台程序也是用这种方式启动的。我可以把代码传给你。你帮我看看,可否?我QQ:756385819