Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/21.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

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/google-sheets/3.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# Console.Out和Console.Error在.NET 4.5中编写的Windows服务中存在争用条件错误_C#_.net_Console_Windows Services - Fatal编程技术网

C# Console.Out和Console.Error在.NET 4.5中编写的Windows服务中存在争用条件错误

C# Console.Out和Console.Error在.NET 4.5中编写的Windows服务中存在争用条件错误,c#,.net,console,windows-services,C#,.net,Console,Windows Services,我在生产中遇到了一个奇怪的问题,windows服务随机挂起,如果您能提供根本原因分析方面的帮助,我将不胜感激 该服务是用C#编写的,并部署到带有.NET4.5的机器上(尽管我也可以用.NET4.5.1复制它) 报告的错误是: Probable I/O race condition detected while copying memory. The I/O package is not thread safe by default. In multithreaded applications

我在生产中遇到了一个奇怪的问题,windows服务随机挂起,如果您能提供根本原因分析方面的帮助,我将不胜感激

该服务是用C#编写的,并部署到带有.NET4.5的机器上(尽管我也可以用.NET4.5.1复制它)

报告的错误是:

Probable I/O race condition detected while copying memory. 
The I/O package is not thread safe by default. 
In multithreaded applications, a stream must be accessed in a thread-safe way, such as a thread-safe wrapper returned by TextReader's or TextWriter's Synchronized methods. 
This also applies to classes like StreamWriter and StreamReader.
我已经将异常的来源缩小到在记录器中对Console.WriteLine()和Console.Error.WriteLine()的调用。这些线程从多个线程调用,在高负载下,错误开始出现,服务挂起

然而,据我所知,整个控制台类是线程安全的(我以前在多个线程中使用过它,没有问题)。此外,当运行与控制台应用程序相同的代码时,不会出现此问题;仅从windows服务。最后,异常的堆栈跟踪显示了对console类中SyncTextWriter的内部调用,该类应该是异常中提到的同步版本

有人知道我是做错了还是漏掉了一点吗?一个可能的解决方法似乎是将Out和Err流重定向到/dev/null,但我更喜欢更详细的分析,这似乎超出了我对.NET的了解

我创建了一个复制windows服务,在尝试时会抛出错误。代码如下

服务类别:

[RunInstaller(true)]
public partial class ParallelTest : ServiceBase
{
    public ParallelTest()
    {
        InitializeComponent();
        this.ServiceName = "ATestService";
    }

    protected override void OnStart(string[] args)
    {
        Thread t = new Thread(DoWork);
        t.IsBackground = false;

        this.EventLog.WriteEntry("Starting worker thread");
        t.Start();

        this.EventLog.WriteEntry("Starting service");
    }

    protected override void OnStop()
    {
    }

    private void DoWork()
    {
        this.EventLog.WriteEntry("Starting");
        Parallel.For(0, 1000, new ParallelOptions() { MaxDegreeOfParallelism = 10 }, (_) =>
        {
            try
            {
                for (int i = 0; i < 10; i++)
                {
                    Console.WriteLine("test message to the out stream");
                    Thread.Sleep(100);
                    Console.Error.WriteLine("Test message to the error stream");
                }
            }
            catch (Exception ex)
            {
                this.EventLog.WriteEntry(ex.Message, EventLogEntryType.Error);
                //throw;
            }
        });
        this.EventLog.WriteEntry("Finished");
    }
}
[运行安装程序(true)]
公共部分类并行测试:ServiceBase
{
公共测试()
{
初始化组件();
this.ServiceName=“ATestService”;
}
启动时受保护的覆盖无效(字符串[]args)
{
螺纹t=新螺纹(定位销);
t、 IsBackground=false;
this.EventLog.WriteEntry(“启动工作线程”);
t、 Start();
this.EventLog.WriteEntry(“启动服务”);
}
受保护的覆盖void OnStop()
{
}
私房
{
this.EventLog.WriteEntry(“启动”);
Parallel.For(0,1000,new ParallelOptions(){MaxDegreeOfParallelism=10},()=>
{
尝试
{
对于(int i=0;i<10;i++)
{
Console.WriteLine(“输出流的测试消息”);
睡眠(100);
Console.Error.WriteLine(“错误流的测试消息”);
}
}
捕获(例外情况除外)
{
this.EventLog.WriteEntry(例如Message,EventLogEntryType.Error);
//投掷;
}
});
this.EventLog.WriteEntry(“完成”);
}
}
主要类别:

static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    static void Main()
    {
        // Remove comment below to stop the errors
        //Console.SetOut(new StreamWriter(Stream.Null));
        //Console.SetError(new StreamWriter(Stream.Null));

        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[] 
        { 
            new ParallelTest() 
        };
        ServiceBase.Run(ServicesToRun);
    }
}
静态类程序
{
/// 
///应用程序的主要入口点。
/// 
静态void Main()
{
//删除下面的注释以停止错误
//控制台设置(新StreamWriter(Stream.Null));
//SetError(新StreamWriter(Stream.Null));
ServiceBase[]ServicesToRun;
ServicesToRun=新的ServiceBase[]
{ 
新的并行测试()
};
ServiceBase.Run(ServicesToRun);
}
}
安装程序类别:

partial class ProjectInstaller
{
    /// <summary>
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.IContainer components = null;

    /// <summary> 
    /// Clean up any resources being used.
    /// </summary>
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    #region Component Designer generated code

    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
        this.serviceProcessInstaller1 = new System.ServiceProcess.ServiceProcessInstaller();
        this.serviceInstaller1 = new System.ServiceProcess.ServiceInstaller();
        // 
        // serviceProcessInstaller1
        // 
        this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
        this.serviceProcessInstaller1.Password = null;
        this.serviceProcessInstaller1.Username = null;
        // 
        // serviceInstaller1
        // 
        this.serviceInstaller1.ServiceName = "ATestServiceHere";
        // 
        // ProjectInstaller
        // 
        this.Installers.AddRange(new System.Configuration.Install.Installer[] {
        this.serviceProcessInstaller1,
        this.serviceInstaller1});

    }

    #endregion

    private System.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller1;
    private System.ServiceProcess.ServiceInstaller serviceInstaller1;
}
部分类项目安装程序
{
/// 
///必需的设计器变量。
/// 
private System.ComponentModel.IContainer components=null;
///  
///清理所有正在使用的资源。
/// 
///如果应释放托管资源,则为true;否则为false。
受保护的覆盖无效处置(布尔处置)
{
if(处理和(组件!=null))
{
组件。Dispose();
}
基地。处置(处置);
}
#区域组件设计器生成的代码
/// 
///设计器支持所需的方法-不修改
///此方法的内容与代码编辑器一起使用。
/// 
私有void InitializeComponent()
{
this.serviceProcessInstaller1=新系统.ServiceProcess.ServiceProcessInstaller();
this.serviceInstaller1=新系统.ServiceProcess.ServiceInstaller();
// 
//serviceProcessInstaller1
// 
this.serviceProcessInstaller1.Account=System.ServiceProcess.ServiceAccount.LocalSystem;
this.serviceProcessInstaller1.Password=null;
this.serviceProcessInstaller1.Username=null;
// 
//服务安装程序1
// 
this.serviceInstaller1.ServiceName=“ATestServiceHere”;
// 
//投射器
// 
this.Installers.AddRange(新的System.Configuration.Install.Installer[]{
此文件为.serviceProcessInstaller1,
此文件为.serviceInstaller1});
}
#端区
专用系统.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller1;
专用System.ServiceProcess.ServiceInstaller serviceInstaller1;
}

使用InstallUtil.exe安装并启动此服务会将错误记录在事件日志中。

Console.Out和Console.Error都是线程安全的,因为它们都会为控制台输出和错误流TextWriter返回线程安全包装(通过TextWriter.Synchronized)。但是,只有当Console.Out和Console.Error是不同流的文本编写器时,此线程安全才适用

代码作为Windows服务运行时引发异常的原因是,在这种情况下,输出和错误TextWriter都设置为StreamWriter.Null,这是一个单例。代码同时调用Console.WriteLine和Console.Error.WriteLine,当一个线程在另一个线程调用Console.Error.WriteLine的同时调用Console.WriteLine时,会导致异常。这会导致同一流同时从2个线程写入,导致“复制内存时检测到可能的I/O争用情况。”异常。如果仅使用Console.WriteLine或仅使用Console.Error.WriteLine,您将发现该异常不再发生

using System;
using System.IO;
using System.Threading.Tasks;

class Program
{
    static void Main(string[] args)
    {
        var oldOut = Console.Out;
        var oldError = Console.Error;

        Console.SetOut(StreamWriter.Null);
        Console.SetError(StreamWriter.Null);
        Parallel.For(0, 2, new ParallelOptions() { MaxDegreeOfParallelism = 2 }, (_) =>
        {
            try
            {
                while(true)
                {
                    Console.WriteLine("test message to the out stream");
                    Console.Error.WriteLine("Test message to the error stream");
                }
            }
            catch (Exception ex)
            {
                Console.SetOut(oldOut);
                Console.SetError(oldError);
                Console.WriteLine(ex);
                Environment.Exit(1);
            }
        });
    }
}