读书人

子线程异步回调的有关问题

发布时间: 2012-01-24 23:11:54 作者: rapoo

子线程异步回调的问题

C# code
    //主窗口    public partial class frmMain : Form    {        private System.Windows.Forms.TreeView tvChapterSelector;[color=#339966]//初始化语句略[/color]         OpenStoryFile osfOpenStoryFile = new OpenStoryFile();        public frmMain()        {            InitializeComponent();        }        private void frmMain_Load(object sender, EventArgs e)        {            osfOpenStoryFile.OpenStoryFileComplete += new OpenStoryFile.OpenStoryFileCompleteHandle(osfOpenStoryFile_OpenStoryFileComplete);            osfOpenStoryFile.DoOpenStoryFile(@"F:\小说\科幻\回归千年.txt");        }        void osfOpenStoryFile_OpenStoryFileComplete(System.Collections.ArrayList e)        {            for (Int32 I = ConstantVars.GlogbalConst.iZero; I < alStoryChapter.Count; I++)            {                tvChapterSelector.Nodes.Add((string)alStoryChapter[I] + string.Empty);[color=#FF0000]//这句报错                //在该控件上执行的操作正从错误的线程调用。使用 Control.Invoke 或 Control.BeginInvoke 封送到正确的线程才能执行此操作。[/color]            }        }    }    //异步打开文件的类    public class OpenStoryFile    {        public delegate void OpenStoryFileCompleteHandle(System.Collections.ArrayList e);        public event OpenStoryFileCompleteHandle OpenStoryFileComplete;        private event OpenStoryFileCompleteHandle OpenStoryFileCompletePretreat;        System.Windows.Forms.Control ctrlTurnThread;        System.Threading.Thread thrdOpenFile;        object objForLock = new object();        string StoryFileToOpen;        public OpenStoryFile()        {            ctrlTurnThread = new Control();[color=#993300]//这里创建控件应该表示这个控件是在主线程创建的吧?[/color]            this.OpenStoryFileCompletePretreat += new OpenStoryFileCompleteHandle(OpenStoryFile_OpenStoryFileCompletePretreat);        }        void OpenStoryFile_OpenStoryFileCompletePretreat(OpenStoryFile.OpenStoryFileArgs e)        {            if (ctrlTurnThread.InvokeRequired)            {                MessageBox.Show("ctrlTurnThread.InvokeRequired");[color=#800000]//但是,这句没有执行到[/color]                OpenStoryFileCompleteHandle osfcpT = new OpenStoryFileCompleteHandle(OpenStoryFileCompletePretreat);                ctrlTurnThread.Invoke(osfcpT, new object[] { e });            }            else            {                MessageBox.Show("ctrlTurnThread.InvokeRequired Treated");[color=#800000]//直接跑这来了。为什么?[/color]                OpenStoryFileComplete(e);            }        }        public void DoOpenStoryFile(string StoryFileToOpen)        {            this.StoryFileToOpen = StoryFileToOpen;            thrdOpenFile = new System.Threading.Thread(new System.Threading.ThreadStart(OpenStoryFileAsyn));            thrdOpenFile.Start();        }        private void OpenStoryFileAsyn()        {            lock (objForLock)            {                if (System.IO.File.Exists(StoryFileToOpen))                {                    try                    {                        System.Collections.ArrayList alStoryChapter = new System.Collections.ArrayList();                        [color=#0000FF]//打开文件并作相应处理后,把内容填充到alStoryChapter[/color]                    }                    OpenStoryFileCompletePretreat(alStoryChapter);[color=#0000FF]//调用委托[/color]                }            }        }    }


[解决办法]
调用InvokeRequired的时候,
先检查控件的句柄(一种Win32窗口句柄)是否已经创建了,你的情况下IsHandleCreated为假,因为该控件还没有初次显示。
如果句柄还没有创建,则尝试检查控件的父控件,而你的情况没有父控件(没有执行过Controls.Add( ctrlTurnThread )之类的语句)。
如果还没有找到,InvokeRequired就直接返回false了。

InvokeRequired需要一个窗口句柄来调用API以便知道该窗口所属的线程号,然后比较该线程号和当前线程号,就可以得出需不需要Invoke了。
该API就是GetWindowThreadProcessId()如果你感兴趣的话

[解决办法]
在第一次走OpenStoryFile_OpenStoryFileCompletePretreat的时候,控件并没有被其他线程使用,没有涉及到资源同步问题



如果这时候,你让线程SEEP一定时间,紧接着走第二次OpenStoryFile_OpenStoryFileCompletePretreat,你会发现InvokeRequired变成TRUE了

读书人网 >C#

热点推荐