简介:

Task 对象是一种的中心思想基于任务的异步模式首次引入.NET Framework 4 中。 因为由执行工作Task对象通常以异步方式执行线程池线程上而不是以同步方式在主应用程序线程中,可以使用Status属性,并将IsCanceled, IsCompleted,和IsFaulted属性,以确定任务的状态。

一.Task的创建

1.创建Task类

(1)

            Task task = new Task(() =>
{
Console.WriteLine("hello world!");
});
task.Start(); 

(2)

            new Task(() =>
{
Console.WriteLine("hello world!");
}).Start();

(3)带参数

            new Task(x =>
{
Console.WriteLine(x.ToString());
}, "hello world!").Start();

(4)带返回值

            Task<string> t = new Task<string>(x =>
{
return x.ToString();
}, "hello world!");
t.Start();
Console.WriteLine(t.Result);

  

2.Task.Factory.StartNew

(1)

            Task.Factory.StartNew(() =>
{
Console.WriteLine("hello world!");
});

(2)带参数

            Task.Factory.StartNew(x =>
{
Console.WriteLine(x.ToString());
}, "hello world!");

  

(3)带返回值

            Task<string> t = Task.Factory.StartNew<string>(() =>
{
return "hello world!";
});
        Console.WriteLine(t.Result);

  

3.Task.Run

            Task.Run(() =>
{
Console.WriteLine("hello world!");
});

4.TaskStatus

        Created = 0, //该任务已初始化,但尚未被计划。
WaitingForActivation = 1, //该任务正在等待 .NET Framework 基础结构在内部将其激活并进行计划。
WaitingToRun = 2,//该任务已被计划执行,但尚未开始执行。
Running = 3, //该任务正在运行,但尚未完成。
WaitingForChildrenToComplete = 4,//该任务已完成执行,正在隐式等待附加的子任务完成。
RanToCompletion = 5,//已成功完成执行的任务。
Canceled = 6, //该任务已通过对其自身的 CancellationToken 引发 OperationCanceledException 对取消进行了确认,此时该标记处于已发送信号状态;或者在该任务开始执行之前,已向该任务的CancellationToken 发出了信号。 有关详细信息,请参阅任务取消。
Faulted = 7 //由于未处理异常的原因而完成的任务。

  

二. TaskCreationOptions

Task.Factory.StartNew和创建Task类可以带TaskCreationOptions参数而Task.Run不可以带

        //
// 摘要:
// 指定应使用默认行为。
None = 0,
//
// 摘要:
// 提示 System.Threading.Tasks.TaskScheduler 以一种尽可能公平的方式安排任务,这意味着较早安排的任务将更可能较早运行,而较晚安排运行的任务将更可能较晚运行。
PreferFairness = 1,
//
// 摘要:
// 指定任务将是长时间运行的、粗粒度的操作,涉及比细化的系统更少、更大的组件。 它会向 System.Threading.Tasks.TaskScheduler
// 提示,过度订阅可能是合理的。 可以通过过度订阅创建比可用硬件线程数更多的线程。 它还将提示任务计划程序:该任务需要附加线程,以使任务不阻塞本地线程池队列中其他线程或工作项的向前推动。
LongRunning = 2,
//
// 摘要:
// 指定将任务附加到任务层次结构中的某个父级。 默认情况下,子任务(即由外部任务创建的内部任务)将独立于其父任务执行。 可以使用 System.Threading.Tasks.TaskContinuationOptions.AttachedToParent
// 选项以便将父任务和子任务同步。 请注意,如果使用 System.Threading.Tasks.TaskCreationOptions.DenyChildAttach
// 选项配置父任务,则子任务中的 System.Threading.Tasks.TaskCreationOptions.AttachedToParent 选项不起作用,并且子任务将作为分离的子任务执行。
// 有关详细信息,请参阅附加和分离的子任务。
AttachedToParent = 4,
//
// 摘要:
// 指定任何尝试作为附加的子任务执行(即,使用 System.Threading.Tasks.TaskCreationOptions.AttachedToParent
// 选项创建)的子任务都无法附加到父任务,会改成作为分离的子任务执行。 有关详细信息,请参阅附加和分离的子任务。
DenyChildAttach = 8,
//
// 摘要:
// 防止环境计划程序被视为已创建任务的当前计划程序。 这意味着像 StartNew 或 ContinueWith 创建任务的执行操作将被视为 System.Threading.Tasks.TaskScheduler.Default
// 当前计划程序。
HideScheduler = 16

1. LongRunning

任务是长时间任务,就需要用LongRunning,可能会创建一个非线程池线程来执行该任务,防止阻塞线程池队列中的其他线程

        private static void fun8()
{
Task.Factory.StartNew(() =>
{
Console.WriteLine($"task1.线程 id {Thread.CurrentThread.ManagedThreadId}. 是否为线程池线程: {Thread.CurrentThread.IsThreadPoolThread}");
}); Task.Factory.StartNew(() =>
{
Console.WriteLine($"task2.线程 id {Thread.CurrentThread.ManagedThreadId}. 是否为线程池线程: {Thread.CurrentThread.IsThreadPoolThread}");
}, TaskCreationOptions.LongRunning);
}

 运行结果:

2. 父子任务(AttachedToParent,DenyChildAttach)

AttachedToParent:将子任务附加到父任务上,表现为:附加到父任务上的所有子任务都结束,父任务才结束

DenyChildAttach:不允许子任务附加到父任务上

(1)子任务不附加到父任务

        private static void fun5()
{
Task t = Task.Factory.StartNew(() =>
{
Console.WriteLine("parent"); Task.Factory.StartNew(() =>
{
Thread.Sleep(1000);
Console.WriteLine("child");
});
});
t.ContinueWith(x =>
{
Console.WriteLine("parent over");
});
}

 运行结果:

(2)子任务附加到父任务上,使用AttachedToParent

        private static void fun6()
{
Task t = Task.Factory.StartNew(() =>
{
Console.WriteLine("parent"); Task.Factory.StartNew(() =>
{
Thread.Sleep(1000);
Console.WriteLine("child");
},TaskCreationOptions.AttachedToParent);
});
t.ContinueWith(x =>
{
Console.WriteLine("parent over");
});
}

 运行结果:

(3)拒绝子任务附加到父任务上,使用DenyChildAttach

        private static void fun7()
{
Task t = Task.Factory.StartNew(() =>
{
Console.WriteLine("parent"); Task.Factory.StartNew(() =>
{
Thread.Sleep(1000);
Console.WriteLine("child");
}, TaskCreationOptions.AttachedToParent);
}, TaskCreationOptions.DenyChildAttach);
t.ContinueWith(x =>
{
Console.WriteLine("parent over");
});
}

  运行结果:

三.CancellationToken 取消任务

        private static void fun4()
{
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
CancellationToken Token = cancellationTokenSource.Token;
//结束任务回调
Token.Register(() =>
{
Console.WriteLine("canceled");
}); Task.Factory.StartNew(() =>
{
try
{
while (true)
{
Console.WriteLine("hello world!");
Thread.Sleep(100);
Token.ThrowIfCancellationRequested();
}
}
catch (OperationCanceledException ocex)
{
}
catch (ObjectDisposedException odex)
{
}
catch (Exception ex)
{
} }, Token); Thread.Sleep(1000); cancellationTokenSource.Cancel();
}

 

执行结果:

当执行cancellationTokenSource.Cancel()后,任务进行到Token.ThrowIfCancellationRequested()代码后,throw出OperationCanceledException异常,才结束任务并执行cancel回调

四.方法

Wait 等待 System.Threading.Tasks.Task 完成执行过程
WaitAll  等待提供的所有 System.Threading.Tasks.Task 对象完成执行过程
WaitAny  等待提供的任一 System.Threading.Tasks.Task 对象完成执行过程
WhenAll  创建一个任务,该任务将在所有 System.Threading.Tasks.Task 对象都完成时完成
WhenAny  任何提供的任务已完成时,创建将完成的任务
ContinueWith  创建一个在目标 System.Threading.Tasks.Task 完成时异步执行的延续任务

1 Wait

阻塞当前线程,等待任务执行完成

        //等待 System.Threading.Tasks.Task 在指定的毫秒数内完成执行。
public bool Wait(int millisecondsTimeout);
//等待 System.Threading.Tasks.Task 完成执行过程。 如果在任务完成之前取消标记已取消,等待将终止。
public void Wait(CancellationToken cancellationToken);
//等待 System.Threading.Tasks.Task 完成执行过程。 如果在任务完成之前超时间隔结束或取消标记已取消,等待将终止。
public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken);
//等待 System.Threading.Tasks.Task 完成执行过程。
public void Wait();
//等待 System.Threading.Tasks.Task 在指定的时间间隔内完成执行。
public bool Wait(TimeSpan timeout);

使用方式:

            t.Wait();//无限等待
t.Wait(100);//等待100ms
t.Wait(TimeSpan.FromMilliseconds(1500));//等待1500ms

例:

        private static void fun9()
{
Task t = Task.Factory.StartNew(() =>
{
Thread.Sleep(1000);
Console.WriteLine("task");
});
t.Wait();
Console.WriteLine("main");
}

 运行结果:

2.WaitAll

阻塞当前线程,等待所有任务执行完成

        //等待提供的所有 System.Threading.Tasks.Task 对象完成执行过程。
public static void WaitAll(params Task[] tasks);
//等待所有提供的可取消 System.Threading.Tasks.Task 对象在指定的时间间隔内完成执行。
public static bool WaitAll(Task[] tasks, TimeSpan timeout);
//等待所有提供的 System.Threading.Tasks.Task 在指定的毫秒数内完成执行。
public static bool WaitAll(Task[] tasks, int millisecondsTimeout);
//等待提供的所有 System.Threading.Tasks.Task 对象完成执行过程(除非取消等待)。
public static void WaitAll(Task[] tasks, CancellationToken cancellationToken);
//等待提供的所有 System.Threading.Tasks.Task 对象在指定的毫秒数内完成执行,或等到取消等待。
public static bool WaitAll(Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken);

  使用:

        private static void fun10()
{
Task t1 = Task.Factory.StartNew(() =>
{
Thread.Sleep(100);
Console.WriteLine("task1");
}); Task t2 = Task.Factory.StartNew(() =>
{
Thread.Sleep(150);
Console.WriteLine("task2");
}); //Task.WaitAll(t1, t2);
//Task.WaitAll(new Task[] { t1, t2 }, 200);
Task.WaitAll(new Task[] { t1, t2 }, TimeSpan.FromMilliseconds(200));
Console.WriteLine("main");
}

  

运行结果:

3.WaitAny

阻塞当前线程,等待任一任务执行完成

        //等待提供的任一 System.Threading.Tasks.Task 对象完成执行过程。
public static int WaitAny(params Task[] tasks);
//等待任何提供的 System.Threading.Tasks.Task 对象在指定的时间间隔内完成执行。
public static int WaitAny(Task[] tasks, TimeSpan timeout);
//等待任何提供的 System.Threading.Tasks.Task 对象在指定的毫秒数内完成执行。
public static int WaitAny(Task[] tasks, int millisecondsTimeout);
//等待提供的任何 System.Threading.Tasks.Task 对象完成执行过程(除非取消等待)。
public static int WaitAny(Task[] tasks, CancellationToken cancellationToken);
//等待提供的任何 System.Threading.Tasks.Task 对象在指定的毫秒数内完成执行,或等到取消标记取消。
public static int WaitAny(Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken);

 使用:

        private static void fun11()
{
Task t1 = Task.Factory.StartNew(() =>
{
Thread.Sleep(100);
Console.WriteLine("task1");
}); Task t2 = Task.Factory.StartNew(() =>
{
Thread.Sleep(150);
Console.WriteLine("task2");
}); //Task.WaitAny(t1, t2);
//Task.WaitAny(new Task[] { t1, t2 }, 200);
Task.WaitAny(new Task[] { t1, t2 }, TimeSpan.FromMilliseconds(200));
Console.WriteLine("main");
}

 结果:

4.WhenAll

不阻塞当前线程,等待所有任务执行完成后,可以进行ContinueWith

        //创建一个任务,该任务将在可枚举集合中的所有 System.Threading.Tasks.Task 对象都完成时完成。
public static Task WhenAll(IEnumerable<Task> tasks);
//创建一个任务,该任务将在数组中的所有 System.Threading.Tasks.Task 对象都完成时完成。
public static Task WhenAll(params Task[] tasks);
//创建一个任务,该任务将在可枚举集合中的所有 System.Threading.Tasks.Task`1 对象都完成时完成。
public static Task<TResult[]> WhenAll<TResult>(IEnumerable<Task<TResult>> tasks);
//创建一个任务,该任务将在数组中的所有 System.Threading.Tasks.Task`1 对象都完成时完成。
public static Task<TResult[]> WhenAll<TResult>(params Task<TResult>[] tasks);

  使用:

(1)不带返回值

public static Task WhenAll(params Task[] tasks);
        private static void fun12()
{
Task t1 = Task.Factory.StartNew(() =>
{
Thread.Sleep(100);
Console.WriteLine("task1");
}); Task t2 = Task.Factory.StartNew(() =>
{
Thread.Sleep(150);
Console.WriteLine("task2");
}); Task.WhenAll(t1, t2).ContinueWith(t =>
{
Console.WriteLine("WhenAll");
}); Console.WriteLine("main");
}

  执行结果:

(2)带返回值

public static Task<TResult[]> WhenAll<TResult>(params Task<TResult>[] tasks);
        private static void fun13()
{
Task<string> t1 = Task.Factory.StartNew(() =>
{
Thread.Sleep(100);
Console.WriteLine("task1");
return "task1";
}); Task<string> t2 = Task.Factory.StartNew<string>(() =>
{
Thread.Sleep(150);
Console.WriteLine("task2");
return "task2";
}); Task.WhenAll(new Task<string>[] { t1, t2 }).ContinueWith(t =>
{
string s = "WhenAll:";
foreach (string item in t.Result)
{
s += item;
}
Console.WriteLine(s);
}); Console.WriteLine("main");
}

  执行结果:

5.WhenAny

        //任何提供的任务已完成时,创建将完成的任务。
public static Task<Task> WhenAny(params Task[] tasks);
//任何提供的任务已完成时,创建将完成的任务。
public static Task<Task> WhenAny(IEnumerable<Task> tasks);
//任何提供的任务已完成时,创建将完成的任务。
public static Task<Task<TResult>> WhenAny<TResult>(params Task<TResult>[] tasks);
//任何提供的任务已完成时,创建将完成的任务。
public static Task<Task<TResult>> WhenAny<TResult>(IEnumerable<Task<TResult>> tasks);

 使用:

(1)不带参数

public static Task<Task> WhenAny(params Task[] tasks);
        private static void fun14()
{
Task t1 = Task.Factory.StartNew(() =>
{
Thread.Sleep(100);
Console.WriteLine("task1");
}); Task t2 = Task.Factory.StartNew(() =>
{
Thread.Sleep(150);
Console.WriteLine("task2");
}); Task.WhenAny(t1, t2).ContinueWith(t =>
{
Console.WriteLine("WhenAny1");
}); Console.WriteLine("main");
}

  运行结果:

(2)带参数:

public static Task<Task<TResult>> WhenAny<TResult>(params Task<TResult>[] tasks);
private static void fun15()
{
Task<string> t1 = Task.Factory.StartNew(() =>
{
Thread.Sleep(100);
Console.WriteLine("task1");
return "response task1";
}); Task<string> t2 = Task.Factory.StartNew<string>(() =>
{
Thread.Sleep(150);
Console.WriteLine("task2");
return "response task2";
}); Task.WhenAny(new Task<string>[] { t1, t2 }).ContinueWith(t =>
{
t.Result.ContinueWith(tt =>
{
Console.WriteLine(tt.Result);
});
}); Console.WriteLine("main");
} 

运行结果:

6.ContinueWith

相当于回调

6.1使用

(1)使用lambda表达式方式

        private static void fun1()
{
Console.WriteLine("start");
Task<string> task1 = new Task<string>(() =>
{
Console.WriteLine("task0");
return "task1";
});
Task<string> task2 = task1.ContinueWith(t =>
{
Console.WriteLine(t.Result);
return "task2";
});
task1.Start();
Console.WriteLine("end");
Console.ReadKey();
}

2.使用函数

 private static void fun2()
{
Console.WriteLine("start");
Task<string> task1 = new Task<string>(doTask1);
Task<string> task2 = task1.ContinueWith(doTask2);
task1.Start();
Console.WriteLine("end");
Console.ReadKey();
} private static string doTask1()
{
Console.WriteLine("task0");
return "task1";
} private static string doTask2(Task<string> t)
{
Console.WriteLine(t.Result);
return "task2";
}

  运行结果:

6.2 ContineWith和task可能不在同一线程上

例:

 private static void fun16()
{
Task.Factory.StartNew(() =>
{
Console.WriteLine($"task {Thread.CurrentThread.ManagedThreadId}");
}).ContinueWith(t =>
{
Console.WriteLine($"ContinueWith {Thread.CurrentThread.ManagedThreadId}");
});
}

  运行结果:

七.TaskFactory类

方法:

StartNew 创建并启动任务
ContinueWhenAll 创建一个延续任务,该任务在一组指定的任务完成后开始
ContinueWhenAny 创建一个延续 System.Threading.Tasks.Task,它将在提供的组中的任何任务完成后马上开始
FromAsync 创建一个 System.Threading.Tasks.Task`1,表示符合异步编程模型模式的成对的开始和结束方法

1.ContinueWhenAll

相当于回调

效果其实和WhenAll差不多,只不过ContineWhenAll采用了回调的方式

使用:带返回值

        private static void fun17()
{
Task<string> t1 = Task.Factory.StartNew<string>(() =>
{
Console.WriteLine("task1");
return "task1";
}); Task<string> t2 = Task.Factory.StartNew<string>(() =>
{
Console.WriteLine("task2");
return "task2";
}); Task.Factory.ContinueWhenAll(new Task[] { t1, t2 }, t =>
{
string s = "ContinueWhenAll:";
foreach (Task<string> item in t)
{
s += item.Result;
}
Console.WriteLine(s);
});
}

  运行结果:

2.ContinueWhenAny

相当于回调

效果其实和WhenAny差不多,只不过ContineWhenAny采用了回调的方式

使用:

        private static void fun18()
{
Task<string> t1 = Task.Factory.StartNew<string>(() =>
{
Thread.Sleep(10);
Console.WriteLine("task1");
return "task1";
}); Task<string> t2 = Task.Factory.StartNew<string>(() =>
{
Console.WriteLine("task2");
return "task2";
}); Task.Factory.ContinueWhenAny(new Task[] { t1, t2 }, t =>
{
Console.WriteLine($"{(t as Task<string>).Result} 先执行完");
});
}

  执行结果:

3.FromAsync

相当于异步委托的精简写法,其中ContinueWith相当于异步委托中的callback

使用:

public Task<TResult> FromAsync<TArg1, TResult>(Func<TArg1, AsyncCallback, object, IAsyncResult> beginMethod, Func<IAsyncResult, TResult> endMethod, TArg1 arg1, object state);

        static void Main(string[] args)
{
fun20(); Console.WriteLine("Main");
Console.ReadKey();
} private static void fun20()
{
var func = new Func<string, string>(x =>
{
Thread.Sleep(1000);
Console.WriteLine(x);
return "callback";
}); Task.Factory.FromAsync(func.BeginInvoke, func.EndInvoke, "func", null).ContinueWith(t =>
{
Console.WriteLine(t.Result);
});
}

  运行结果:

参考:

https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.tasks.task?view=netframework-4.7.2

https://docs.microsoft.com/zh-cn/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap?view=netframework-4.7.2

https://www.cnblogs.com/leo_wl/archive/2012/03/03/2378695.html#_label2

C#Task学习的更多相关文章

  1. c# .Net并行和多线程编程之Task学习记录!

    任务Task和线程Thread的区别: 1.任务是架构在线程之上的,也就是说任务最终还是要抛给线程去执行. 2.任务跟线程不是一对一的关系,比如开10个任务并不是说会开10个线程,这一点任务有点类似线 ...

  2. 微软BI 之SSIS 系列 - Execute SQL Task 中的 Single Row 与 Full Result Set 的处理技巧

    开篇介绍 Execute SQL Task 这个控件在微软BI ETL 项目中使用的频率还是非常高的,也是大部分入门 SSIS 初学者最早接触到的几个控制流控件. 我们通常使用 Execute SQL ...

  3. [.net 多线程]Task

    C# 异步编程Task整理(一) c# .Net并行和多线程编程之Task学习记录! .NET 实现并行的几种方式(一) Dispatcher介绍 [C#学习笔记]使用C#中的Dispatcher 用 ...

  4. 一个demo学会js

    全栈工程师开发手册 (作者:栾鹏) 快捷链接: js系列教程1-数组操作全解 js系列教程2-对象和属性全解 js系列教程3-字符串和正则全解 js系列教程4-函数与参数全解 js系列教程5-容器和算 ...

  5. js系列教程2-对象、构造函数、对象属性全解

    全栈工程师开发手册 (作者:栾鹏) 快捷链接: js系列教程1-数组操作全解 js系列教程2-对象和属性全解 js系列教程3-字符串和正则全解 js系列教程4-函数与参数全解 js系列教程5-容器和算 ...

  6. Siamese Neural Networks for One-shot Image Recognition

    one-shot learning简介 这是迁移学习的两种极端形式 zero-shot learning 指的是我们之前没有这个类别的训练样本,但是我们可以学习到一个映射X->Y, 如果这个映射 ...

  7. Js基础知识2-对象、对象属性全解

    Object对象 Object对象包含如下属性和方法,也就意味着一切对象(函数也是对象)都包含如下方法. 每种方法和属性在不同的对象中有不同的作用,并不是每种对象都有使用每个方法的必要. 下面是Obj ...

  8. Tokio,Rust异步编程实践之路

    缘起 在许多编程语言里,我们都非常乐于去研究在这个语言中所使用的异步网络编程的框架,比如说Python的 Gevent.asyncio,Nginx 和 OpenResty,Go 等,今年年初我开始接触 ...

  9. C#线程和异步

    C#Thread学习 C#ThreadPool学习 C#Task学习 C#backgroundWorker c# 锁的使用 C#前台线程和后台线程区别 C#Async,await异步简单介绍 C#委托 ...

随机推荐

  1. 【转】wireshark抓包工具详细说明及操作使用

      wireshark是非常流行的网络封包分析软件,功能十分强大.可以截取各种网络封包,显示网络封包的详细信息.使用wireshark的人必须了解网络协议,否则就看不懂wireshark了. 为了安全 ...

  2. Vue.js:起步

    ylbtech-Vue.js:起步 1.返回顶部 1. Vue.js 起步 每个 Vue 应用都需要通过实例化 Vue 来实现. 语法格式如下: var vm = new Vue({ // 选项 }) ...

  3. 配置MapReduce插件时,弹窗报错org/apache/hadoop/eclipse/preferences/MapReducePreferencePage : Unsupported major.minor version 51.0(Hadoop2.7.3集群部署)

    原因: hadoop-eclipse-plugin-2.7.3.jar 编译的jdk版本和eclipse启动使用的jdk版本不一致导致.  解决方案一: 修改myeclipse.ini文件即可解决. ...

  4. DataTable改变column类型

    1.必须先克隆DataTable 2.列换类型 3.逐行往新DataTable赋值,并转换某列类型 如: DataTable dt = diorg.Clone(); //必须先克隆,此时并不包含数据 ...

  5. phpStudy启动失败时的解决方法 提示缺vc9运行库

    问题描述: 问题产生原因分析: php5.3.5.4和apache都是用vc9编译,电脑必须安装vc9运行库才能运行. php5.5.5.6是vc11编译,如用php5.5.5.6必须安装vc11运行 ...

  6. Android 4学习(8):用户界面 - Fragment

    参考<Professional Android 4 Development> Fragment简介 Fragment是我们可以将Activity分成不同的组成部分,这些组成部分拥有自己的生 ...

  7. 使用ReentrantReadWriteLock类

    读读共享 类ReentrantReadWriteLock的使用:写写互斥 读写互斥

  8. 转摘:ashx+jquery-autocomplete文本框输入提示功能Asp.net

    引入所需文件 <script type="text/javascript" src="JS/jquery-1.8.2.min.js"></sc ...

  9. 清除苹果手机input的默认样式

    在手机端上写了一个页面,按钮的地方是用input标签button按钮,给的是绿色的背景颜色,在安卓手机上显示正常,在苹果手机上显示不正常,如下图 解决办法: css加上下面这一行代码就可以了,inpu ...

  10. log4j配置文件的手动加载与配置初始化

    一. 本地项目: 初始化log4j的日志配置,指定到src目录下(建议用2)         //1. 本地项目-属性文件配置         PropertyConfigurator.configu ...