欢迎光临
我们一直在努力

使用.NET Remoting实现并行计算-.NET教程,远程及网络应用

建站超值云服务器,限时71元/月

[简介]

过去,做一个并行计算的试验要费九牛二虎之力,今天,有了.net remoting,我们只需要完成非常少的编程工作,便可以跨多台计算机轻松进行分布计算。在本文中,eric bergman-terrell创建了一个名为digits of pi的应用程序,它使用并行的多台计算机以不可思议的精度计算π值。他设法在12小时内完成了10,000位数的计算,却只使用了相当少的计算资源。这比用一台计算机单独完成计算快了300%。

欢迎进入.net remoting的奇妙世界!在这篇文章里,您将与我一起,亲自动手体验并行计算的威力。为了方便您更好地理解这篇文章,请首先按照下面的步骤作一番准备:

1.从附增光盘获取示例应用程序及源代码。

2.打开everything.sln解决方案。此解决方案包含运行“digits of pi”应用程序所需的三个项目(client、server和serverloader)。还包含一个名为simpleclient的项目。加载everything.sln之后,请选择build(编译)| batch build…(批编译…)。单击select all(全部选定)按钮,然后单击build(编译)。编译所有内容后,请在本地计算机以及您的lan中的远程计算机上安装该软件。

3.在本地计算机上,创建一个文件夹并将以下文件复制到其中:

server\bin\release\plouffe_bellard.dll

client\bin\release\digitsofpi.exe

4.在每个远程计算机和本地计算机上,创建一个文件夹并将以下文件复制到其中:

server\bin\release\plouffe_bellard.dll

serverloader\bin\release\serverloader.exe

serverloader\serverloader.exe.config

5.然后运行serverloader.exe程序。当然,运行serverloader和digits of pi程序之前,需要在每台计算机上安装.net framework。

在所有远程计算机和本地计算机上运行serverloader程序后,请运行digits of pi程序。单击configure…(配置…)(参见图1),添加本地计算机名和远程计算机名。如果不确定某台计算机的名称,请查看serverloader程序,它在表中显示其计算机名。如果您很幸运地拥有一个多cpu系统,您只需为所有cpu输入一次计算机名。只需在计算机名后键入@符号和一个编号。例如,如果您拥有一个名为“brainiac”的双cpu系统,则键入以下计算机名:“brainiac@1”和“brainiac@2”。为多cpu系统输入多个计算机名可以确保所有计算机的cpu都用于计算π值。输入所有计算机名后,单击ok(确定)。

指定要计算的位数(参见图2)并单击calculate(计算)。请从较少的位数开始,π值小数点后面的位数越多,程序所需的时间就越长。

图3显示了digits of pi程序如何在本地计算机和远程计算机中分配工作量,它使用tcp/ip端口9000发送请求并接收结果。接下来,我们将详细探讨remoting、plouffe_bellard服务器对象、serverloader程序、simpleclient程序和digits of pi程序。

服务器对象

服务器对象将计算指定的九位π值。它被命名为plouffe_bellard,因为它使用fabrice bellard的增强simon plouffe算法。虽然存在更快的算法,但plouffe-bellard算法非常简单(少于300行源代码),它使用少量的内存,并且由于九位数字可以单独计算,因此更适于并行执行。plouffe_bellard.calculatepidigits方法将计算在指定位置开始的九位π值。例如,calculatepidigits(0)从第一位开始返回九位数字:141592653。calculatepidigits(9)从第十位开始返回九位数字,依此类推。

serverloader

serverloader程序将加载服务器对象,指定通过lan访问服务器对象的协议和端口,侦听来自客户端程序的传入调用,处理调用并返回结果。特别值得注意的是,所有这些只需一行代码便可完成,只需通过使用配置文件的路径调用remotingconfiguration.configure方法。serverloader程序将加载名为serverloader.exe.config的配置文件(参见代码段1)。此配置文件指定以singlecall模式加载服务器对象,即每个传入调用都由服务器对象的一个新实例处理。如果服务器对象以singleton模式加载,每个传入调用都将由同一个实例处理。类型属性指定服务器对象的完整类型名称(包括pb命名空间)及其程序集的名称。objecturi属性指定对象的统一资源标识符(uri)的端点。<channel>元素指定使用tcp协议,端口9000访问服务器对象。

代码段1:serverloader.exe.config

<configuration>

<system.runtime.remoting>

<application name = "serverloader">

<service>

<wellknown

mode="singlecall"

type="pb.plouffe_bellard,plouffe_bellard"

objecturi="plouffe_bellard"/>

</service>

<channels>

<channel ref="tcp server" port="9000"/>

</channels>

</application>

</system.runtime.remoting>

</configuration>

simpleclient

我创建了一个名为simpleclient的程序,以说明客户端程序访问远程计算机上的服务器对象是多么容易。要运行simpleclient,首先在远程计算机上运行serverloader,然后在本地计算机上运行simpleclient.exe程序。在remote machine(远程计算机)文本框中输入远程计算机的名称,然后单击calculate(计算)按钮开始计算第一个九位π值。simpleclient的calculatebutton_click方法包含客户端访问远程服务器所需的所有代码(参见代码段2)。可以使用由远程计算机名、协议(tcp)和端口号(9000)组成的url访问远程服务器。例如,要访问我的“pentium 200”计算机,则url为“tcp://pentium 200:9000/serverloader/plouffe_bellard”。创建url后,将使用服务器的类型(plouffe_bellard)和url调用activator.getobject。然后,返回的值被转换为plouffe_bellard对象以备使用。调用其calculatepidigits方法时,请求被发送到远程计算机上的serverloader。然后,服务器对象计算小数位。最后,在一个文本框中显示返回客户端程序的结果。

代码段2:用于访问远程服务器的simpleclient代码

private void calculatebutton_click(object sender,system.eventargs e)

{

cursor.current = cursors.waitcursor;

plouffe_bellard picalculator = null;

string machinename = remotemachinetextbox.text;

try

{

int port = 9000;

string url = "tcp://" + machinename + ":" +

port + "/serverloader/plouffe_bellard";

picalculator = (plouffe_bellard)

activator.getobject(typeof(plouffe_bellard), url);

resultstextbox.text = "3." +

picalculator.calculatepidigits(1);

}

catch(exception)

{

messagebox.show(

"需要在计算机" +

machinename,

"simple client上运行serverloader.exe",

messageboxbuttons.ok,

messageboxicon.error);

}

cursor.current = cursors.arrow;

}

digits of pi客户端

digits of pi客户端程序比simpleclient更复杂。simpleclient仅通过访问远程计算机上的服务器对象来计算前九位π值。而digits of pi则同时使用configure(配置)对话框中指定的远程计算机和本地计算机(如图1所示)并行计算用户指定的小数位。服务器对象在单独的线程中访问,以便在可能需要很长时间的计算过程中保持digits of pi gui对用户操作的响应性。

digits of pi使用数组将作业分为九位数据块,将工作量分配到所有可用的计算机上。用户单击calculate(计算)按钮后,将创建solutionarray(参见图4)。solutionarray为要计算的每组九位π值分配一个solutionitem元素。服务器对象计算m_digit字段指定的九位数组后,数位将存储在m_results成员中。m_machinename成员包含运行服务器的计算机的名称。存储计算机名是为了使digits of pi能够显示每台计算机计算的小数总数(参见图2)。

为使服务器对象并行计算,digits of pi将为每个服务器对象创建一个线程并启动线程计算。然后,必须等待所有线程完成计算后才能显示最终结果。waithandle对于等待多个线程很有用。digits of pi将为每个线程使用一个waithandle,以等待所有线程完成计算。

将调用calculationthread.calculate(参见代码段3)以便为每个服务器对象创建一个线程。该操作将启动线程运行,然后返回一个autoresetevent(从waithandle衍生而来)。每个线程的autoresetevent都存储在一个数组中,然后数组被传递给waithandle.waitall。完成线程计算后,将对其autoresetevent调用set方法。最后一个线程调用set方法后,将返回waitall调用,并显示π的值。

代码段3:calculationthread。

public static waithandle calculate(

solutionarray solutionarray, string machinename)

{

calculationthread calculationthread = new

calculationthread(solutionarray, machinename);

thread thread = new thread(new

threadstart(calculationthread.calculate));

thread.start();

return calculationthread.calculationdone;

}

每个线程都使用相同的算法:如果有更多的工作要处理,线程将夺取下一个solutionitem,在solutionitem中存储服务器对象的计算机名,计算指定的九位小数,并将结果存储在solutionitem中。此进程将一直运行,直到所有solutionitem中都填充了结果。有关详细信息,请参见代码段4。

代码段4:calculationthread.calculate

public void calculate()

{

plouffe_bellard picalculator =

remotepicalculator.getpicalculator(

getrealmachinename(machinename));

if (picalculator != null)

{

solutionitem item = null;

bool abort;

do

{

abort = solutionarray.abort;

if (!abort)

{

item = solutionarray.getnextitem();

if (item != null)

{

item.machinename = machinename;

try

{

item.results =

picalculator.calculatepidigits(item.digit);

}

catch (exception e)

{

abort = true;

messagebox.show(

"无法访问主机上的远程对象" +

machinename +

environment.newline +

environment.newline +

"message: " +

e.message, globals.programname,

messageboxbuttons.ok,

messageboxicon.error);

}

updatestatisticsdelegate usd = new

updatestatisticsdelegate(

mf.updatestatistics);

mf.invoke(usd, new object[] {} );

}

}

} while (item != null && !abort);

calculationdone.set();

}

}

下面是每一步的说明:

1. getrealmachinename从多cpu计算机名中删除@1模式。例如,getrealmachinename("brainiac@1")返回“brainiac”。有关多cpu计算机名的解释,请参见图1对话框中的文本。

2.知道正确的计算机名后,将其传递给remotepicalculator.

getpicalculator,这样才可以通过picalculator变量访问该计算机上的服务器对象。

3. 如果用户单击了cancel(取消)按钮,将设置abort属性。如果abort属性为true,线程将停止计算。

4. 对mf.invoke的调用使线程可以安全地更新listview中的统计数据(参见图2),即使该listview是由另一个线程创建的。在32位windows编程中,绝不允许在创建某个控件的线程之外处理该控件。

5. 完成循环(即计算完指定的所有π位数或者用户单击cancel [取消]按钮)后,将调用线程的autoresetevent的set函数。

6. 当每个线程都调用其autoresetevent的set函数后,将返回对waithandle.waitall的调用并显示结果。

线程同步

如果digits of pi的代码由多个线程同时访问,可能会有多个地方出现错误。例如,如果两个线程同时调用solutionarray.getnextitem,可能会返回相同的内容。这就是在getnextitem方法中设置[methodimpl(methodimploptions.synchronized)]属性的原因,该属性可以确保一次只有一个线程调用该方法。如果方法的每一行代码都不应由多个线程同时访问,则使方法同步是一个很好的策略。

由于mainform.calculate方法只有一行代码不能同时被多个线程访问,因此它将在该行代码之前调用monitor.enter,并在其后调用monitor.exit。如果该行代码已在其他线程上运行,monitor.enter将被阻止。如果整个函数已实现同步,那么只保护需要防止多个线程访问的代码行就可以提高性能。

从system.windows.forms.control派生的对象(例如button、textbox、richtextbox、label、listbox、listview等等)只应由创建它们的线程处理。要从非创建线程中安全处理control衍生对象,请首先将处理代码放入一个方法,然后为该方法声明一个代理:

delegate void setresultstextdelegate(string text);

private void setresultstext(string text)

{

resultsrichtextbox.text = text;

}

然后使用form.invoke间接调用该方法:

setresultstextdelegate srtd = new

setresultstextdelegate(setresultstext);

invoke(srtd, new object[] { "" } );

invoke方法将从创建它的线程中调用该方法,它使用的参数与对象数组中的元素相对应。

小结

.net remoting是一种在远程(和本地)计算机上执行代码简单有效的机制。只需将代码封装到.net对象中,编写加载该对象并侦听请求的程序,然后在客户端程序中调用activator.getobject。如果您的lan中有一些闲置的计算机,可以利用它们轻松地解决并行问题。只需记住要使用正确的线程同步机制,以防止线程之间发生冲突。

赞(0)
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com 特别注意:本站所有转载文章言论不代表本站观点! 本站所提供的图片等素材,版权归原作者所有,如需使用,请与原作者联系。未经允许不得转载:IDC资讯中心 » 使用.NET Remoting实现并行计算-.NET教程,远程及网络应用
分享到: 更多 (0)

相关推荐

  • 暂无文章