前言
最近在学习Web Api框架的时候接触到了async/await,这个特性是.NET 4.5引入的,由于之前对于异步编程不是很了解,所以花费了一些时间学习一下相关的知识,并整理成这篇博客,如果在阅读的过程中发现不对的地方,欢迎大家指正。
同步编程与异步编程
通常情况下,我们写的C#代码就是同步的,运行在同一个线程中,从程序的第一行代码到最后一句代码顺序执行。而异步编程的核心是使用多线程,通过让不同的线程执行不同的任务,实现不同代码的并行运行。
前台线程与后台线程
关于多线程,早在.NET2.0时代,基础类库中就提供了Thread实现。默认情况下,实例化一个Thread创建的是前台线程,只要有前台线程在运行,应用程序的进程就一直处于运行状态,以控制台应用程序为例,在Main方法中实例化一个Thread,这个Main方法就会等待Thread线程执行完毕才退出。而对于后台线程,应用程序将不考虑其是否执行完毕,只要应用程序的主线程和前台线程执行完毕就可以退出,退出后所有的后台线程将被自动终止。来看代码应该更清楚一些:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("主线程开始");
//实例化Thread,默认创建前台线程
Thread t1 = new Thread(DoRun1);
t1.Start();
//可以通过修改Thread的IsBackground,将其变为后台线程
Thread t2 = new Thread(DoRun2) { IsBackground = true };
t2.Start();
Console.WriteLine("主线程结束");
}
static void DoRun1()
{
Thread.Sleep(500);
Console.WriteLine("这是前台线程调用");
}
static void DoRun2()
{
Thread.Sleep(1500);
Console.WriteLine("这是后台线程调用");
}
}
}运行上面的代码,可以看到DoRun2方法的打印信息“这是后台线程调用”将不会被显示出来,因为应用程序执行完主线程和前台线程后,就自动退出了,所有的后台线程将被自动终止。这里后台线程设置了等待1.5s,假如这个后台线程比前台线程或主线程提前执行完毕,对应的信息“这是后台线程调用”将可以被成功打印出来。
Task
.NET 4.0推出了新一代的多线程模型Task。async/await特性是与Task紧密相关的,所以在了解async/await前必须充分了解Task的使用。这里将以一个简单的Demo来看一下Task的使用,同时与Thread的创建方式做一下对比。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Threading;
using System.Threading.Tasks;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("主线程启动");
//.NET 4.5引入了Task.Run静态方法来启动一个线程
Task.Run(() => { Thread.Sleep(1000); Console.WriteLine("Task1启动"); });
//Task启动的是后台线程,假如要在主线程中等待后台线程执行完毕,可以调用Wait方法
Task task = Task.Run(() => { Thread.Sleep(500); Console.WriteLine("Task2启动"); });
task.Wait();
Console.WriteLine("主线程结束");
}
}
}首先,必须明确一点是Task启动的线程是后台线程,不过可以通过在Main方法中调用task.Wait()方法,使应用程序等待task执行完毕。Task与Thread的一个重要区分点是:Task底层是使用线程池的,而Thread每次实例化都会创建一个新的线程。这里可以通过这段代码做一次验证:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Threading;
using System.Threading.Tasks;
namespace TestApp
{
class Program
{
static void DoRun1()
{
Console.WriteLine("Thread Id =" + Thread.CurrentThread.ManagedThreadId);
}
static void DoRun2()
{
Thread.Sleep(50);
Console.WriteLine("Task调用Thread Id =" + Thread.CurrentThread.ManagedThreadId);
}
static void Main(string[] args)
{
for (int i = 0; i < 50; i++)
{
new Thread(DoRun1).Start();
}
for (int i = 0; i < 50; i++)
{
Task.Run(() => { DoRun2(); });
}
//让应用程序不立即退出
Console.Read();
}
}
}运行代码,可以看到DoRun1()方法每次的Thread Id都是不同的,而DoRun2()方法的Thread Id是重复出现的。我们知道线程的创建和销毁是一个开销比较大的操作,Task.Run()每次执行将不会立即创建一个新线程,而是到CLR线程池查看是否有空闲的线程,有的话就取一个线程处理这个请求,处理完请求后再把线程放回线程池,这个线程也不会立即撤销,而是设置为空闲状态,可供线程池再次调度,从而减少开销。
Task<TResult>
Task<TResult>是Task的泛型版本,这两个之间的最大不同是Task<TResult>可以有一个返回值,看一下代码应该一目了然:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Threading;
using System.Threading.Tasks;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("主线程开始");
Task task = Task.Run(() => { Thread.Sleep(1000); return Thread.CurrentThread.ManagedThreadId.ToString(); });
Console.WriteLine(task.Result);
Console.WriteLine("主线程结束");
}
}
}Task<TResult>的实例对象有一个Result属性,当在Main方法中调用task.Result的时候,将等待task执行完毕并得到返回值,这里的效果跟调用task.Wait()是一样的,只是多了一个返回值。
async/await 特性
经过前面的铺垫,终于迎来了这篇文章的主角async/await,还是先通过代码来感受一下这两个特性的使用。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Threading;
using System.Threading.Tasks;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("-------主线程启动-------");
Task task = GetLengthAsync();
Console.WriteLine("Main方法做其他事情");
Console.WriteLine("Task返回的值" + task.Result);
Console.WriteLine("-------主线程结束-------");
}
static async Task GetLengthAsync()
{
Console.WriteLine("GetLengthAsync Start");
string str = await GetStringAsync();
Console.WriteLine("GetLengthAsync End");
return str.Length;
}
static Task GetStringAsync()
{
return Task.Run(() => { Thread.Sleep(2000); return "finished"; });
}
}
}首先来看一下async关键字。async用来修饰方法,表明这个方法是异步的,声明的方法的返回类型必须为:void或Task或Task<TResult>。返回类型为Task的异步方法中无需使用return返回值,而返回类型为Task<TResult>的异步方法中必须使用return返回一个TResult的值,如上述Demo中的异步方法返回一个int。而返回类型可为void,则是为了和事件处理程序兼容,比如下面的示例:
public Form1()
{
InitializeComponent();
btnDo.Click += Down;
}
public async void Down(object sender, EventArgs e)
{
btnDo.Enabled = false;
string str = await Run();
labText.Text = str;
btnDo.Enabled = true;
}
public Task Run()
{
return Task.Run(() => { Thread.Sleep(5000); return DateTime.Now.ToString(); });
}再来看一下await关键字。await必须用来修饰Task或Task<TResult>,而且只能出现在已经用async关键字修饰的异步方法中。
通常情况下,async/await必须成对出现才有意义,假如一个方法声明为async,但却没有使用await关键字,则这个方法在执行的时候就被当作同步方法,这时编译器也会抛出警告提示async修饰的方法中没有使用await,将被作为同步方法使用。了解了关键字async\await的特点后,我们来看一下上述Demo在控制台会输入什么吧。

输出的结果已经很明确地告诉我们整个执行流程了。GetLengthAsync异步方法刚开始是同步执行的,所以"GetLengthAsync Start"字符串会被打印出来,直到遇到第一个await关键字,真正的异步任务GetStringAsync开始执行,await相当于起到一个标记/唤醒点的作用,同时将控制权放回给Main方法,"Main方法做其他事情"字符串会被打印出来。之后由于Main方法需要访问到task.Result,所以就会等待异步方法GetLengthAsync的执行,而GetLengthAsync又等待GetStringAsync的执行,一旦GetStringAsync执行完毕,就会回到await GetStringAsync这个点上执行往下执行,这时"GetLengthAsync End"字符串就会被打印出来。
当然,我们也可以使用下面的方法完成上面控制台的输出。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Threading;
using System.Threading.Tasks;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("-------主线程启动-------");
Task task = GetLengthAsync();
Console.WriteLine("Main方法做其他事情");
Console.WriteLine("Task返回的值" + task.Result);
Console.WriteLine("-------主线程结束-------");
}
static Task GetLengthAsync()
{
Console.WriteLine("GetLengthAsync Start");
Task task = Task.Run(() => { string str = GetStringAsync().Result;
Console.WriteLine("GetLengthAsync End");
return str.Length; });
return task;
}
static Task GetStringAsync()
{
return Task.Run(() => { Thread.Sleep(2000); return "finished"; });
}
}
}对比两种方法,是不是async\await关键字的原理其实就是通过使用一个线程完成异步调用吗?答案是否定的。async关键字表明可以在方法内部使用await关键字,方法在执行到await前都是同步执行的,运行到await处就会挂起,并返回到Main方法中,直到await标记的Task执行完毕,才唤醒回到await点上,继续向下执行。更深入点的介绍可以查看文章末尾的参考文献。
async/await 实际应用
微软已经对一些基础类库的方法提供了异步实现,接下来将实现一个例子来介绍一下async/await的实际应用。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Threading;
using System.Threading.Tasks;
using System.Net;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("开始获取博客园首页字符数量");
Task task1 = CountCharsAsync("http://www.cnblogs.com");
Console.WriteLine("开始获取百度首页字符数量");
Task task2 = CountCharsAsync("http://www.baidu.com");
Console.WriteLine("Main方法中做其他事情");
Console.WriteLine("博客园:" + task1.Result);
Console.WriteLine("百度:" + task2.Result);
}
static async Task CountCharsAsync(string url)
{
WebClient wc = new WebClient();
string result = await wc.DownloadStringTaskAsync(new Uri(url));
return result.Length;
}
}
}参考文献:<IIIustrated C# 2012> 关于async/await的FAQ 《深入理解C#》
问题描述:
当我们的界面需要在程序运行中不断更新数据时,当一个textbox的数据需要变化时,为了让程序执行中不出现界面卡死的现像,最好的方法就是多线程来解决
一个主线程来创建界面,使用一个子线程来执行程序并更新主界面
这样就不会出现卡死的现像了
这肯定是没有问题的,
但是为什么在使用的过程中一样会有很多地方会出现卡死呢,而且有用户跟我说是我的Httphelper类的问题,其实不是,而且我再次声明我的Httphelper类跟多线程并没有关系。不要在诬赖我了哦。
这个问题其实也困或了我很久,但是今天终于解决了,而且我发现很多人有这样的问题,所以我分享一个例子方便大家参考吧。
先来看看我的界面

当我单击
开始执行后

这个时候界面是不会卡死的,
只是数据在不断的更新
下面看看我的代码
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 System.Threading;
namespace WindowsFormsApplication3
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//创建一个委托,是为访问TextBox控件服务的。
public delegate void UpdateTxt(string msg);
//定义一个委托变量
public UpdateTxt updateTxt;
//修改TextBox值的方法。
public void UpdateTxtMethod(string msg)
{
richTextBox1.AppendText(msg + "\r\n");
richTextBox1.ScrollToCaret();
}
//此为在非创建线程中的调用方法,其实是使用TextBox的Invoke方法。
public void ThreadMethodTxt(int n)
{
this.BeginInvoke(updateTxt, "线程开始执行,执行" + n + "次,每一秒执行一次");
for (int i = 0; i < n; i++)
{
this.BeginInvoke(updateTxt, i.ToString());
//一秒 执行一次
Thread.Sleep(1000);
}
this.BeginInvoke(updateTxt, "线程结束");
}
//开启线程
private void button1_Click(object sender, EventArgs e)
{
Thread objThread = new Thread(new ThreadStart(delegate
{
ThreadMethodTxt(Convert.ToInt32(textBox1.Text.Trim()));
}));
objThread.Start();
}
private void Form1_Load_1(object sender, EventArgs e)
{
//实例化委托
updateTxt = new UpdateTxt(UpdateTxtMethod);
}
}
}
就这些代码,大家看注释应该就明白一点了,
主要是使用一个委托来更新界面的richTextBox1
这样写是肯定没有问题的,而且在我其它的更高级一点的例子里也是这么写的
C#多线程|匿名委托传参数|测试网站压力--升级版
http://www.sufeinet.com/thread-13-1-1.html
上面的文件大家可以做为参考
那问题现在那里呢,其实就出在这一句上
this.BeginInvoke(updateTxt, "线程结束");
大家也许已经发现了,我是这样写的,而不是
updateTxt("线程结束");
这样来直接在子线程中使用,
我相信有很多同志都是这样写的,其实错就错在这里
如果直接使用
updateTxt("线程结束");
大家想一下应该就明白了,
updateTxt是在主线程创建的,而我们在子线程中直接使用,运行的数据多了,就会出现卡死,这是界面信息堵死的原因,
所以就算是委托也不能直接在子线程中使用,而是要使用BeginInvoke方法来调用这个委托
这样才不会出现卡死的现像。
问题就解决了。
大家支持一下哦
下面是我的源码提供给大家下载吧
WindowsFormsApplication3.zip (49.65 KB)
// 定义
public enum status { ready, ordered, printed }public enum statusB { ready = 1, ordered = 3, printed = 5 }// 将字符串转化为枚举类型
public static status? ConvertStringToEnum_status(string str)
{
return str != null && Enum.IsDefined(typeof(status), str) ? (status)Enum.Parse(typeof(status), str) : null as status?;
}// 将数字(索引值)转化为枚举类型
status s = (status)1; // 值为 status.orderedstatusB s = (statusB)1; // 值为 statusB.ready// 将枚举类型转化为数字(索引值)
int s = (int)status.ordered; // 值为 1int s = (int)statusB.ready; // 值为 1// 循环
foreach (status s in Enum.GetValues(typeof(status)))
{
}当无法序列化或需要序列化为字符串时,在定义枚举前加上:
当使用 System.Text.Json 时为:
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.JsonStringEnumConverter))]当使用 Newtonsoft.Json 时为:
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]随着拥有多个硬线程CPU(超线程、双核)的普及,多线程和异步操作等并发程序设计方法也受到了更多的关注和讨论。本文主要是想与园中各位高手一同探讨一下如何使用并发来最大化程序的性能。
多线程和异步操作的异同
多线程和异步操作两者都可以达到避免调用线程阻塞的目的,从而提高软件的可响应性。甚至有些时候我们就认为多线程和异步操作是等同的概念。但是,多线程和异步操作还是有一些区别的。而这些区别造成了使用多线程和异步操作的时机的区别。
异步操作的本质
所有的程序最终都会由计算机硬件来执行,所以为了更好的理解异步操作的本质,我们有必要了解一下它的硬件基础。 熟悉电脑硬件的朋友肯定对DMA这个词不陌生,硬盘、光驱的技术规格中都有明确DMA的模式指标,其实网卡、声卡、显卡也是有DMA功能的。DMA就是直接内存访问的意思,也就是说,拥有DMA功能的硬件在和内存进行数据交换的时候可以不消耗CPU资源。只要CPU在发起数据传输时发送一个指令,硬件就开始自己和内存交换数据,在传输完成之后硬件会触发一个中断来通知操作完成。这些无须消耗CPU时间的I/O操作正是异步操作的硬件基础。所以即使在DOS这样的单进程(而且无线程概念)系统中也同样可以发起异步的DMA操作。
线程的本质
线程不是一个计算机硬件的功能,而是操作系统提供的一种逻辑功能,线程本质上是进程中一段并发运行的代码,所以线程需要操作系统投入CPU资源来运行和调度。
异步操作的优缺点
因为异步操作无须额外的线程负担,并且使用回调的方式进行处理,在设计良好的情况下,处理函数可以不必使用共享变量(即使无法完全不用,最起码可以减少共享变量的数量),减少了死锁的可能。当然异步操作也并非完美无暇。编写异步操作的复杂程度较高,程序主要使用回调方式进行处理,与普通人的思维方式有些初入,而且难以调试。
多线程的优缺点
多线程的优点很明显,线程中的处理程序依然是顺序执行,符合普通人的思维习惯,所以编程简单。但是多线程的缺点也同样明显,线程的使用(滥用)会给系统带来上下文切换的额外负担。并且线程间的共享变量可能造成死锁的出现。
适用范围
在了解了线程与异步操作各自的优缺点之后,我们可以来探讨一下线程和异步的合理用途。我认为:当需要执行I/O操作时,使用异步操作比使用线程+同步I/O操作更合适。I/O操作不仅包括了直接的文件、网络的读写,还包括数据库操作、Web Service、HttpRequest以及.Net Remoting等跨进程的调用。
而线程的适用范围则是那种需要长时间CPU运算的场合,例如耗时较长的图形处理和算法执行。但是往往由于使用线程编程的简单和符合习惯,所以很多朋友往往会使用线程来执行耗时较长的I/O操作。这样在只有少数几个并发操作的时候还无伤大雅,如果需要处理大量的并发操作时就不合适了。
http://npoi.codeplex.com/documentation
免费,开源,无需安装 Office,功能强大,兼容性好,使用方便,可以在 Visual Studio 的 NuGet 管理器中直接搜索安装。
导出 Excel 简单示例:
HSSFWorkbook book = new HSSFWorkbook();
ISheet sheet = book.CreateSheet("工作表1");
IRow r0 = sheet.CreateRow(0);
r0.CreateCell(0).SetCellValue("行1列1");
r0.CreateCell(1).SetCellValue("行1列2");
IRow r1 = sheet.CreateRow(1);
r1.CreateCell(0).SetCellValue("行2列1");
r1.CreateCell(2).SetCellValue("行2列3");
MemoryStream ms = new MemoryStream();
book.Write(ms);
Response.AddHeader("Content-Disposition", string.Format("attachment; filename={0}.xls", "文件名"));
Response.BinaryWrite(ms.ToArray());
Response.End();
book = null;
ms.Close();
ms.Dispose();字体相关:
ICellStyle style = book.CreateCellStyle();
IFont font = book.CreateFont();
font.FontHeightInPoints = 20; // 字体大小
font.Color = HSSFColor.White.Index; // 字体颜色
style.SetFont(font);
style.FillForegroundColor = HSSFColor.Red.Index; // 背景色
style.FillPattern = FillPattern.SolidForeground;
cell.CellStyle = style;合并单元格:
CellRangeAddress cra = new CellRangeAddress(r - 1, r - 1, c - 1, c);
sheet.AddMergedRegion(cra);宽度自适应:
for (int i = 1; i < c; i++) {
sheet.AutoSizeColumn(i);
}函数:
cell.SetCellFormula("SUM(B2:B4)");顺便提一句,ICellStyle 和 IFont 最好一种样式一个变量先定义好再用,不要在循环里疯狂地 Create,否则超出数量(512个)会导致样式失效。
其它参考:http://www.ez2o.com/Blog/Post/csharp-Excel-NPOI-Font-Style
wxy0510:经过用户表优化后,论坛不活跃用户将被转至存档表。这将意味着论坛在常规调用、查询都不会遍历这些存档表的数据。这也大大提高服务器工作效率,减少服务器负载。当然处于存档表的用户在执行一次登录操作后,数据会重新转入主表。
xoyozo:这个登录是指论坛本身的用户登录,如果用第三方客户端 App 登录一般是不起作用的(除非 App 考虑到了这个情况)
common_member 是主表,common_member_archive 是存档表,这两个表的用户数相加即为 UC 用户表 ucenter_members 中的用户总和。
Demo:http://blueimp.github.io/jQuery-File-Upload/
VS 用户可以直接在 NuGet 包管理器中获得
jQuery File Upload in Asp.Net C#:http://codehandbook.org/jquery-file-upload-asp-net-csharp/
Options:https://github.com/blueimp/jQuery-File-Upload/wiki/Options
本文是一个初学者对苹果开发证书的理解,有一定局限性,不适合所有苹果开发者参考,欢迎批评指正。
发布 iOS 应用主要有三块内容:Certificates, Identifiers & Profiles
Certificates(证书):是用来给应用程序签名的
Identifiers(标识符):一个应用对应一个 ID,相当于应用程序的身份证
Provisioning Profiles(描述文件):它将证书、标识符、设备结合起来,形成一个描述文件,让 Xcode 知道需要打一个怎么样的 .ipa 包
如何创建证书
创建 App ID 就不详说了,不要使用带通配符的名称。值得一提的是,如果要使用推送,必须勾选“Push Notifications”,其它功能按需勾选。
创建证书,需要用到 Mac 电脑上的“钥匙串访问”来生成一个“本地证书”(CSR 文件):
打开“钥匙串访问” - 证书助理 - 从证书颁发机构请求证书 - 保存到磁盘
然后就可以拿这个本地证书去 Apple Developer 里生成“开发证书”或“发布证书”(CER 文件)。打包证书和推送证书生成过程类似,区别是选择“App Store and Ad Hoc”还是“Apple Push Notification service SSL”。下载后添加到钥匙串就完成了。
添加测试设备就不提了。
然后就是描述文件,同样有开发和发布两种。开发主要是用于 Xcode 环境中;发布中还分两种:App Store 是用于正式上线的,Ad Hoc 是用于测试设备的。下一步选择 App ID,下一步选择证书,然后给 Profile 取个能够分辨的名字,生成的是一个 mobileprovision 文件。
提供证书文件给第三方打包平台
一般需要提供“iOS Distribution 证书”、“推送证书”、“Ad Hoc”和“App Store”类型的 Profiles、App ID 以及 iTunes Connect 中的 Apple ID(不是指登录的邮箱,是指在 iTunes Connect 中创建的 App 在商店中的 ID)。
证书其实跟 App ID 不是一一对应的关系,多个 App 是可以使用同一个证书的。可以单独创建,到期互不影响。Download 后是 .cer 格式,用“钥匙串”工具可以导出 .p12 证书并设置密码。
如果由于命名无序或创建错误导致无法分辨上架中的 App 用的是哪个证书,哪个描述文件,那么,首先要知道这个 App ID 是多少(就是 iTunes Connect 中所谓的“套装 ID”),然后在 Provisioning Profiles 中挨个查看对应的 App ID,点击“Edit”还能看到使用的证书。
有些平台需要 .pem 文件,是由 .p12 文件通过一个命令转化而来的,具体可以百度。
证书过期怎么办
创建一个呗,然后描述文件 Edit,选择新的证书。
推送证书过期怎么办
在 App ID 里 Edit 就能查看 Push Notifications 项的 SSL Certificate 是否过期。如果过期需要重新创建证书,可以直接在 App ID 的 Edit 里快捷创建(需要“钥匙串”配合)。
不用重新提交 App Store 审核
换一台电脑,证书要怎么导过去
这个问题适用于自己使用 Xcode 开发应用的情况,暂时问一下百度吧。
注:本文部分总结来自百度,未经证实。
Apple Push Services 和 APNs Production iOS 这两种类型有什么区别?
“/”应用程序中的服务器错误。
未能加载文件或程序集“XXXXXX”或它的某一个依赖项。试图加载格式不正确的程序。
说明: 执行当前 Web 请求期间,出现未经处理的异常。请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误的出处的详细信息。
异常详细信息: System.BadImageFormatException: 未能加载文件或程序集“XXXXXX”或它的某一个依赖项。试图加载格式不正确的程序。
源错误:
执行当前 Web 请求期间生成了未经处理的异常。可以使用下面的异常堆栈跟踪信息确定有关异常原因和发生位置的信息。
程序集加载跟踪: 下列信息有助于确定程序集“XXXXXX”未能加载的原因。
警告: 程序集绑定日志记录被关闭。 要启用程序集绑定失败日志记录,请将注册表值 [HKLM\Software\Microsoft\Fusion!EnableLog] (DWORD)设置为 1。 注意: 会有一些与程序集绑定失败日志记录关联的性能损失。 要关闭此功能,请移除注册表值 [HKLM\Software\Microsoft\Fusion!EnableLog]。
当使用 Visual Studio 发布网站时,可能会遇到上述黄屏报错,原因之一是引用的 dll 路径不正确,可以用 Release 模式生成一次看看;原因之二是应用程序的位数与服务器的不匹配。
一般来说用“Any CPU”的方式没有问题,但遇到上述报错的百度网友都很轻松地通过修改 IIS 应用程序池的“启用 32 位应用程序”来解决这个问题,原因是他们的服务器是 64 位操作系统。当我这次在 32 位操作系统的服务器上遇到这个问题的时候真的是手足无措了,然后一阵乱配置,偶尔还能成功跑起来,于是仔细对比了发布到服务器上的文件,发现只有 bin 目录下的 dll 文件有区别,而导致这些区别的原因是我在发布时把“Release - Any CPU”换成了“Debug - Any CPU”,所以一时找不到更好的解决办法的朋友不妨也试试这个方法,只是会有一点点担心性能问题。有更好的解决方案的朋友也不要忘了联系我。



资源中心