Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.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#_Multithreading_Task_Invoke - Fatal编程技术网

C# 线程安全日志记录无法在调试器中使用任务或线程

C# 线程安全日志记录无法在调试器中使用任务或线程,c#,multithreading,task,invoke,C#,Multithreading,Task,Invoke,我尝试将任务或线程中的文本消息记录到表单上的文本框中。为此,我使用Invoke和invokererequired方法与主线程同步,这在internet上的许多示例中都可以找到。请参见下面的LogMessage\u委托和LogMessage\u Threadsafe。当我关闭应用程序时,布尔标志finished被设置为true,任务/线程应该停止工作 在我在Form1\u FormClosing事件处理程序(finished=true;)的第一行设置断点之前,这一切都可以正常工作。然后我只看到控制

我尝试将
任务
线程
中的文本消息记录到表单上的文本框中。为此,我使用
Invoke
invokererequired
方法与主线程同步,这在internet上的许多示例中都可以找到。请参见下面的
LogMessage\u委托
LogMessage\u Threadsafe
。当我关闭应用程序时,布尔标志
finished
被设置为true,任务/线程应该停止工作

在我在
Form1\u FormClosing
事件处理程序(
finished=true;
)的第一行设置断点之前,这一切都可以正常工作。然后我只看到控制台消息“logmessageinvokererequired”,但没有相应的“LogMessage”,应用程序挂起

如果我注释掉
LogMessage\u Threadsafe
调用
Work
(仅控制台消息),则它会再次工作。应用程序正在关闭,如预期的那样

那么,有人能向我解释一下这种行为吗?我找不到理由

请注意,我在
Form1\u FormClosing
事件处理程序中进行了标记,因此表单仍然有效

namespace MultiThreadedTest
{
    public partial class Form1 : Form
    {
        //************************************************************
        // Fields

        Thread worker = null;
        Task task = null;
        bool finished = false;


        //************************************************************
        // Constructor

        public Form1()
        {
            InitializeComponent();

            worker = new Thread(Work);
            worker.Start();

            //task = Task.Factory.StartNew(Work);
        }


        //************************************************************
        // Helper methods

        public void LogMessage(string sMessage)
        {
            LogTextBox.Text += sMessage + Environment.NewLine;
        }

        /// <summary>
        /// Threadsafe wrapper for LogMessage
        /// </summary>
        delegate void LogMessage_Delegate(string sMessage);
        public void LogMessage_Threadsafe(string sMessage)
        {
            // InvokeRequired required compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (this.InvokeRequired)
            {
                Console.WriteLine("LogMessage InvokeRequired");

                LogMessage_Delegate callback = new LogMessage_Delegate(LogMessage_Threadsafe);
                this.Invoke(callback, new object[] { sMessage });
            }
            else
            {
                Console.WriteLine("LogMessage");

                LogMessage(sMessage);
            }
        }


        //************************************************************
        // Commands

        void Work()
        {
            while (!finished)
            {
                Console.WriteLine("Tread/Task Waiting...");
                LogMessage_Threadsafe("Tread/Task Waiting...");
                Thread.Sleep(1000);  // Wait a little...
            }
            Console.WriteLine("Thread/Task Done");
        }


        //************************************************************
        // Events

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            finished = true;

            if (worker != null) worker.Join();

            if (task != null) Task.WaitAll(task);

            Console.WriteLine("App Done");
        }
    }
}
名称空间多线程测试
{
公共部分类Form1:Form
{
//************************************************************
//田地
线程工作者=null;
Task=null;
bool finished=false;
//************************************************************
//建造师
公共表格1()
{
初始化组件();
工人=新线程(工作);
worker.Start();
//task=task.Factory.StartNew(工作);
}
//************************************************************
//辅助方法
公共无效日志消息(字符串消息)
{
LogTextBox.Text+=sMessage+Environment.NewLine;
}
/// 
///日志消息的线程安全包装器
/// 
delegate void LogMessage_delegate(字符串消息);
public void LogMessage_Threadsafe(字符串消息)
{
//invokererequired比较
//向创建线程的线程ID调用线程。
//如果这些线程不同,则返回true。
if(this.invokererequired)
{
Console.WriteLine(“需要调用日志消息”);
LogMessage\u Delegate callback=新的LogMessage\u Delegate(LogMessage\u Threadsafe);
调用(回调,新对象[]{sMessage});
}
其他的
{
Console.WriteLine(“日志消息”);
日志信息(sMessage);
}
}
//************************************************************
//命令
无效工作()
{
当(!完成)
{
Console.WriteLine(“踏板/任务等待…”);
LogMessage_Threadsafe(“踏板/任务等待…”);
Thread.Sleep(1000);//稍等。。。
}
Console.WriteLine(“线程/任务完成”);
}
//************************************************************
//事件
私有作废Form1\u FormClosing(对象发送方,FormClosingEventArgs e)
{
完成=正确;
如果(worker!=null)worker.Join();
if(task!=null)task.WaitAll(task);
Console.WriteLine(“应用程序完成”);
}
}
}

如果在UI线程中的断点处暂停,则通过
Invoke
封送到UI线程的调用将不会执行,因为它们在暂停的UI线程上运行

但从你的评论来看,这似乎不是问题所在。所以我猜问题是,通过在断点处暂停,您已经允许后台线程进入一种状态,即它在
Invoke()
上阻塞,然后您尝试加入该后台线程,该线程将阻塞,直到
Invoke
完成,这是永远不会发生的


作为一个单独的问题,如果从多个线程访问
finished
,则需要使用
lock
块包围读写,以确保线程安全。

如果在UI线程的断点处暂停,则通过
Invoke
封送到UI线程的调用将不会执行,因为它们在UI线程上运行,暂停

但从你的评论来看,这似乎不是问题所在。所以我猜问题是,通过在断点处暂停,您已经允许后台线程进入一种状态,即它在
Invoke()
上阻塞,然后您尝试加入该后台线程,该线程将阻塞,直到
Invoke
完成,这是永远不会发生的


作为一个单独的问题,如果要从多个线程访问
finished
,则需要使用
lock
块来包围读写,以确保线程安全。

自从.NET 4引入任务以来,您不需要使用原始线程。Invoke也不需要,但自从.NET4.5引入了
async/await
之后,它就过时了。4.5还引入了线程安全进度报告和与的取消,如中所述

Progress
在创建它的线程上调用它的委托,在本例中是UI线程。您可以将接口传递给任何后台方法(任务、线程方法等),并使用它报告进度

考虑到最早支持的.NET版本是4.5.2,您可以假定这些类始终可用。顺便说一句,TLS1.2支持是在4.5.2中添加的,因此任何抵制者都必须升级,因为他们发现自己无法连接到GMail或其他需要TLS1.2的服务

使用这些类可以大大简化代码。带b的快速脏表单
public partial class Form1 : Form
{
    System.Threading.Timer _timer;

    IProgress<string> _progress;

    public Form1()
    {
        InitializeComponent();
        _progress = new Progress<string>(msg => textBox1.Text += msg + "\r\n");
        _timer = new System.Threading.Timer(theCallback);
    }

    private async void theCallback(object state)
    {
        for (int i = 0; i < 5; i++)
        {
            await Task.Delay(100);
            _progress.Report($"Boo {i}");
        }
    }

    private void Form1_Load(object sender, EventArgs e)
    {            
            _timer.Change(0, 10000);
    }

    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        _timer.Dispose();
        _timer = null;
        _progress = null;
    }
}