tcl多线程编程的使用说明
- 格式:pdf
- 大小:296.51 KB
- 文档页数:28
tcl define_proc用法
在Tcl中,`define`不是一个内置的关键字,而是使用命令定义过程的语言。
其中,`proc`命令用于定义一个过程,该过程可以被其他程序或脚本调用。
`proc`命令有多种参数,其中包括过程的名称、输入参数和过程体。
例如,可以使用以下代码定义一个名为"hello"的过程:
```tcl
proc hello {} {puts "Hello, Tcl! "}
```
然后,可以使用"hello"命令调用该过程,以便在命令行中打印出"Hello, Tcl! "。
通过定义和调用多个过程,可以组织Tcl脚本并实现所需的功能,从而编写模块化、可重用的代码。
此外,`define_proc`是Tcl过程定义的一种形式,允许在过程定义中设置一些属性。
这些属性可以包括过程的名称、参数、返回类型和其他信息。
通过使用`define_proc`,可以更精确地控制过程的行为和功能。
tcl 语法除法TCL语法-除法在TCL(Tool Command Language)编程语言中,除法操作是常见且必要的运算之一。
TCL提供了多种方法来执行除法运算,以满足不同的需求。
本文将介绍TCL语法中除法运算的不同形式及其用法。
1. 除法操作符在TCL中,除法可以使用“/”操作符进行计算。
例如,使用以下命令执行整数除法:```set result [expr 10 / 3]```这将返回3,因为10除以3等于3.333,但这是整数除法,结果会被截断为最接近的整数值。
2. 浮点除法要执行浮点除法,可以使用“double”函数将操作数转换为浮点数。
例如:```set result [expr double(10) / double(3)]```这将返回3.3333333333333335作为双精度浮点数。
在此示例中,使用double 函数将整数10和3转换为浮点数进行相除。
3. 向下取整除法有时候我们需要将除法结果向下取整,TCL提供了“floor”函数来满足这个需求。
例如:```set result [expr {floor(10.0 / 3.0)}]```这将返回3.0作为结果。
在这个例子中,我们使用了floor函数将浮点除法的结果向下取整。
4. 取余操作除了整数除法,TCL还提供了取余操作符“%”,用于计算除法的余数。
例如,以下命令计算10除以3的余数:```set remainder [expr 10 % 3]```这将返回1,因为10除以3的商为3,余数为1。
5. 除法的应用场景除法在计算中有广泛的应用场景。
例如,在分配任务给工人时,可以使用除法来确定每个工人应该完成的任务数量。
另一个例子是计算平均值,可以使用除法将总和除以计数来得到平均值。
6. 除法的注意事项在进行除法运算时,需要注意一些细节。
首先,除数不应为零,因为除以零是无效的并且会导致错误。
其次,如果希望获得精确的小数结果,则需要将操作数转换为浮点数。
Tcl用法详述T C L用法祥述一TCL语法1 脚本、命令和单词符号一个TCL脚本可以包含一个或多个命令。
命令之间必须用换行符或分号隔开,下面的两个脚本都是合法的:set a 1set b 2或set a 1;set b 2TCL的每一个命令包含一个或几个单词,第一个单词代表命令名,另外的单词则是这个命令的参数,单词之间必须用空格或TAB键隔开。
TCL解释器对一个命令的求值过程分为两部分:分析和执行。
在分析阶段,TCL 解释器运用规则把命令分成一个个独立的单词,同时进行必要的置换(substitution);在执行阶段,TCL 解释器会把第一个单词当作命令名,并查看这个命令是否有定义,如果有定义就激活这个命令对应的C/C++过程,并把所有的单词作为参数传递给该命令过程,让命令过程进行处理。
2 置换(substitution)注:在下面的所有章节的例子中,'%'为TCL的命令提示符,输入命令回车后,TCL会在接着的一行输出命令执行结果。
'//'后面是我自己加上的说明,不是例子的一部分。
TCL解释器在分析命令时,把所有的命令参数都当作字符串看待,例如:%set x 10 //定义变量x,并把x的值赋为1010%set y x+100 //y的值是x+100,而不是我们期望的110x+100上例的第二个命令中,x被看作字符串x+100的一部分,如果我们想使用x的值'10' ,就必须告诉TCL解释器:我们在这里期望的是变量x的值,而非字符'x'。
怎么告诉TCL解释器呢,这就要用到TCL 语言中提供的置换功能。
TCL提供三种形式的置换:变量置换、命令置换和反斜杠置换。
每种置换都会导致一个或多个单词本身被其他的值所代替。
置换可以发生在包括命令名在内的每一个单词中,而且置换可以嵌套。
1) 变量置换(variable subtitution)变量置换由一个$符号标记,变量置换会导致变量的值插入一个单词中。
Tcl编程教程第一部分:基础知识```tclputs "Hello, Tcl!"```将文件保存为`hello.tcl`。
在命令行中,使用以下命令运行这个程序:```bashtclsh hello.tcl```你将看到输出`Hello, Tcl!`。
3. 变量:在Tcl中,你可以使用`set`命令创建变量。
例如:```tclset name "John"puts $name```输出将会是 `John`。
4. 字符串操作:Tcl提供了许多可以操作字符串的内置命令。
例如,你可以使用`string length`命令来获取字符串的长度:```tclset str "Hello, World!"set len [string length $str]puts "String length: $len"```输出将会是 `String length: 13`。
5. 数字操作:除了操作字符串,Tcl还支持对数字的操作。
你可以使用`expr`命令来进行数学运算:```tclset num1 10set num2 5set sum [expr $num1 + $num2]puts "Sum: $sum"```输出将会是 `Sum: 15`。
第二部分:条件和循环1. 条件语句:在Tcl中,你可以使用`if`语句来进行条件判断。
例如:```tclset num 10if {$num > 0}puts "Positive"} elseif {$num < 0}puts "Negative"} elseputs "Zero"```输出将会是 `Positive`。
2. 循环语句:Tcl中有两种常见的循环语句,`while`循环和`foreach`循环。
tcl中isshare用法概述及解释说明1. 引言1.1 概述本篇长文将详细介绍Tcl中isshare命令的用法,并对其进行解释说明。
在Tcl 编程中,isshare是一个非常重要的命令,它能够检测给定的变量是否是共享变量。
共享变量在并发编程中起到关键作用,因此了解isshare的使用方法以及它与其他Tcl命令的关系对于正确实现并发编程和优化性能至关重要。
1.2 文章结构本文将按照以下结构进行介绍和解释:- 引言部分将提供概述,并明确文章的目的和结构。
- 接下来,我们将深入解释isshare命令的定义、功能以及具体使用方法。
- 我们还将探讨isshare与其他Tcl命令之间的联系和互动方式。
- 在示例和应用场景分析部分,我们将通过示例代码介绍isshare在实际应用中的具体情况,并进行深入解析。
- 同时,在并发编程方面,我们将分析isshare在该领域中的应用场景,并评估它对性能和资源消耗的影响。
- 为了帮助读者避免常见误用和错误排查问题,在常见问题及解决方法部分,我们提供了常见误用和错误排查的指南,并对可能遇到的问题进行解答与说明。
- 此外,我们还将分享最佳实践和优化建议,以便读者能够更好地使用isshare命令。
- 最后,在结论与展望部分,我们对本文的主要观点和发现进行总结,并探讨了Tcl中isshare用法的未来发展趋势。
1.3 目的本文旨在提供关于Tcl中isshare命令用法的详尽说明。
通过全面介绍isshare 命令的定义、功能、使用方法以及与其他Tcl命令的关系,读者将能够全面理解isshare在编程中的重要性和应用场景。
此外,示例代码和应用场景分析将帮助读者更好地理解如何正确使用isshare,同时评估其对性能和资源消耗的影响。
常见问题及解决方法部分提供了避免误用和排查问题时的指南,有助于读者更快地解决潜在问题。
最后,在结论与展望部分,本文将总结主要观点并探讨Tcl中isshare用法未来发展趋势,为读者提供进一步思考和研究方向。
在Tcl(Tool Command Language)中,expect是一个用于自动化交互式程序的工具。
它允许你编写脚本,以模拟用户与另一个程序的交互。
expect可以等待特定的输出模式出现,并根据这些模式作出响应。
下面是expect在Tcl中的基本用法:安装和导入Expect包:首先,确保你的Tcl环境中已经安装了Expect包。
你可以使用package require Expect来导入它。
创建spawn进程:使用spawn命令启动一个外部程序。
例如,spawn ssh user@host会启动一个SSH会话。
使用expect等待模式:使用expect命令等待特定的输出模式。
你可以等待多个模式,并为每个模式定义一个响应。
发送响应:一旦expect匹配到一个模式,你可以使用send命令发送响应。
关闭spawn进程:使用expect eof等待进程结束,或者使用close命令关闭spawn进程。
下面是一个简单的Tcl脚本示例,演示如何使用Expect自动登录到一个SSH服务器:tcl#!/usr/bin/tclsh# 导入Expect包package require Expect# 启动SSH会话spawn ssh user@host# 等待密码提示并发送密码expect "password:"send "your_password\r"# 等待命令提示符并发送命令expect "$ "send "ls\r"# 等待命令执行完成并打印输出expect "$ "puts $expect_out(buffer)# 关闭spawn进程closeexit 0注意:在实际使用中,为了安全起见,不建议在脚本中硬编码密码。
你可以考虑使用SSH密钥认证等更安全的方式。
此外,确保在使用Expect时遵循最佳实践,以避免潜在的安全风险。
↖(^ω^)↗↖(^ω^)↗查看文章TCL语言的一些学习资料TCL语言Tcl tick 的构成-------Tcl是一种很通用的脚本语言,它几乎在所有的平台上都可以解释运行,功能强大。
是tool command language的缩写,发音为"tickle”,实际上包含了两个部分:一个语言和一个库。
首先,Tcl是一种简单的脚本语言,主要使用于发布命令给一些互交程序如文本编辑器、调试器和shell。
它有一个简单的语法和很强可扩充性,Tcl可以创建新的过程以增强其内建命令的能力。
其次,Tcl是一个库包,可以被嵌入应用程序,Tcl的库包含了一个分析器、用于执行内建命令的例程和可以使你扩充(定义新的过程)的库函数。
应用程序可以产生Tcl命令并执行,命令可以由用户产生,也可以从用户接口的一个输入中读取(按钮或菜单等)。
但Tcl库收到命令后将它分解并执行内建的命令,经常会产生递归的调用。
在下面的清单中,将会发现第一个很常见的示例程序,它是用Tcl 实现的。
这是一个完整的脚本:第一行命令调用tclsh 环境,第二行命令执行实际工作。
用您所选择的文本编辑器创建该脚本,输入chmod +x hello.tcl 使之成为可执行文件,然后执行它以测试您这件“作品”。
~/tcltk$ cat hello.tcl#!/usr/bin/tclshputs stdout {Hello, World!}~/tcltk$ ./hello.tclHello, World!Tcl 和Tk 是解释型的、可扩展脚本语言。
与BSD 许可证十分相似,该许可证允许在任何情况下自由使用该软件,只要在所有副本中保留该版权并且在任何分发中一字不差地传递通告。
这个许可证条款使Tcl/Tk 成为自由软件。
Tcl/Tk 是一种解释型环境。
可以通过添加预编译的C 函数来扩展Tcl 解释器,可从Tcl 环境内部调用Tcl 解释器。
可以为特定目的或一般的以及广泛使用的而定制这些扩展。
TCL中文教程范文一、TCL简介TCL是一种解释性的脚本语言,由John Ousterhout于1988年开发。
它被设计用于与其他软件组件进行交互,并提供了强大的编程和脚本功能。
TCL提供了丰富的内置函数和命令,可以用于控制流、字符串处理、文件操作等。
二、TCL语法1.注释在TCL中,使用“#”符号来表示注释。
一行以“#”开头的内容将被视为注释,不会被执行。
2.变量TCL中的变量无需声明即可使用。
您可以使用“set”命令来定义变量,如下所示:```set variable value```例如,要定义一个名为“name”的变量,可以使用以下命令:```set name "John"```类似地,您可以使用“$”符号来使用变量的值,如下所示:```puts $name```3.控制流TCL提供了多种控制流语句,如条件语句(if)、循环语句(while、for)、分支语句(switch)等。
以下是几个示例:-条件语句:```if {$x > $y}puts "x is greater than y"} elseif {$x == $y}puts "x is equal to y"} elseputs "x is less than y"```-循环语句:```set i 0while {$i < 10}puts $iincr ifor {set i 0} {$i < 10} {incr i}puts $i```-分支语句:```switch $option"a"puts "Option a selected"}"b"puts "Option b selected"}defaultputs "Invalid option"}```4.字符串处理TCL提供了丰富的字符串处理功能,如连接字符串、截取字符串、查找子字符串等。
C H A P T E R21 Multi-Threaded Tcl ScriptsThis chapter describes the Thread extension for creating multi-threaded Tcl scripts.This Chapter is from Practical Programming in Tcl and Tk, 4th Ed.Copyright 2003 © Brent Welch, Ken Jones/book/T hread support, a key feature of manylanguages, is a recent addition to Tcl. That’s because the Tcl event loop supports features implemented by threads in most other languages, such as graphical user interface management, multi-client servers, asynchronous communication, and scheduling and timing operations. However, although Tcl’s event loop can replace the need for threads in many circumstances, there are still some instances where threads can be a better solution:•Long-running calculations or other processing, which can “starve” the event loop•Interaction with external libraries or processes that don’t support asynchro-nous communication•Parallel processing that doesn’t adapt well to an event-driven model •Embedding Tcl into an existing multi-threaded applicationWhat are Threads?Traditionally, processes have been limited in that they can do only one thing at a time. If your application needed to perform multiple tasks in parallel, you designed the application to create multiple processes. However, this approach has its drawbacks. One is that processes are relatively “heavy” in terms of the resources they consume and the time it takes to create them. For applications that frequently create new processes — for example, servers that create a new321322 Multi-Threaded Tcl Scripts Chap. 21 process to handle each client connection — this can lead to decreased response time. And widely parallel applications that create many processes can consume so many system resources as to slow down the entire system. Another drawback is that passing information between processes can be slow because most inter-process communication mechanisms — such as files, pipes, and sockets —involve intermediaries such as the file system or operating system, as well as requiring a context switch from one running process to another.Threads were designed as a light-weight alternative. Threads are multiple flows of execution within the same process. All threads within a process share the same memory and other resources. As a result, creating a thread requires far fewer resources than creating a separate process. Furthermore, sharing informa-tion between threads is much faster and easier than sharing information between processes.The operating system handles the details of thread creation and coordina-tion. On a single-processor system, the operating system allocates processor time to each of an application’s threads, so a single thread doesn’t block the rest of the application. On multi-processor systems, the operating system can even run threads on separate processors, so that threads truly can run simultaneously.The drawback to traditional multi-threaded programming is that it can be difficult to design a thread-safe application — that is, an application in which one thread doesn’t corrupt the resources being used by another thread. Because all resources are shared in a multi-threaded application, you need to use various locking and scheduling mechanisms to guard against multiple threads modifying resources concurrently.Thread Support in TclTcl added support for multi-threaded programming in version 8.1. The Tcl core was made thread-safe. Furthermore, new C functions exposed “platform-neutral”thread functionality. However, no official support was provided for multi-threaded scripting. Since then, the Thread extension — originally written by Brent Welch and currently maintained by Zoran Vasiljevic — has become the accepted mechanism for creating multi-threaded Tcl scripts. The most recent version of the Thread extension as this was being written was 2.5. In general, this version requires Tcl 8.3 or later, and several of the commands provided require Tcl 8.4 or later.At the C programming level, Tcl’s threading model requires that a Tcl interpreter be managed by only one thread. However, each thread can create as many Tcl interpreters as needed running under its control. As is the case in even a single-threaded application, each Tcl interpreter has its own set of variables and procedures. A thread can execute commands in another thread’s Tcl inter-preter only by sending special messages to that interpreter’s event queue. Those messages are handled in the order received along with all other types of events.Thread Support in Tcl323 Obtaining a Thread-Enabled Tcl InterpreterMost binary distributions of Tcl are not thread-enabled, because the defaultoptions for building the Tcl interpreters and libraries do not enable thread sup-port. Thread safety adds overhead, slowing down single-threaded Tcl applica-tions, which constitute the vast majority of Tcl applications. Also, many Tclextensions aren’t thread safe, and naively trying to use them in a multi-threadedapplication can cause errors or crashes.Unless you can obtain a thread-enabled binary distribution of Tcl, you mustcompile your own from the Tcl source distribution. This requires running the configure command with the --enable-threads option during the build pro-cess. (See Chapter 48, “Compiling Tcl and Extensions” for more information.)You can test whether a particular Tcl interpreter is thread-enabled bychecking for the existence of the tcl_platform(threaded) element. This ele-ment exists and contains a Boolean true value in thread-enabled interpreters,whereas it doesn't exist in interpreters without thread support.Using Extensions in Multi-Threaded ScriptsBecause each interpreter has its own set of variables and procedures, youmust explicitly load an extension into each thread that wants to use it. Only the Thread extension itself is automatically loaded into each interpreter.You must be careful when using extensions in multi-threaded scripts. ManyTcl extensions aren’t thread-safe. Attempting to use them in multi-threadedscripts often results in crashes or corrupted data.Tcl-only extensions are generally thread-safe. Of course, they must makeno use of other commands or extensions that aren’t thread-safe. But otherwise,multi-threaded operation doesn’t add any new issues that don't already affectsingle-threaded scripts.You should always assume that a binary extension is not thread-safe unlessits documentation explicitly says that it is. And even thread-safe binary exten-sions must be compiled with thread support enabled for you to use them inmulti-threaded applications. (The default compilation options for most binaryextensions don’t include thread support.)Tk isn’t truly thread-safe.Most underlying display libraries (such as X Windows) aren’t thread safe —or at least aren’t typically compiled with thread-safety enabled. However, signif-icant work has gone into making the Tk core thread-safe. The result is that youcan safely use Tk in a multi-threaded Tcl application as long as only one threaduses Tk commands to manage the interface. Any other thread that needs toupdate the interface should send messages to the thread controlling the inter-face.324 Multi-Threaded Tcl Scripts Chap. 21 Getting Started with the Thread ExtensionYou start a thread-enabled tclsh or wish the same as you would a non-threaded tclsh or wish. When started, there is only one thread executing, often referred to as the main thread, which contains a single Tcl interpreter. If you don’t create any more threads, your application runs like any other single-threaded applica-tion.Make sure that the main thread is the last one to terminate.The main thread has a unique position in a multi-threaded Tcl script. If it exits, then the entire application terminates. Also, if the main thread terminates while other threads still exist, Tcl can sometimes crash rather than exiting cleanly. Therefore, you should always design your multi-threaded applications so that your main thread waits for all other threads to terminate before it exits.Before accessing any threading features from your application, you must load the Thread extension:package require ThreadThe Thread extension automatically loads itself into any new threads your application creates with thread::create. All other extensions must be loaded explicitly into each thread that needs to use them. The Thread extension creates commands in three separate namespaces:•The thread namespace contains all of the commands for creating and man-aging threads, including inter-thread messaging, mutexes, and condition variables.•The tsv namespace contains all of the commands for creating and managing thread shared variables.•The tpool namespace contains all of the commands for creating and manag-ing thread pools.Creating ThreadsThe thread::create command creates a new thread containing a new Tcl interpreter. Any thread can create another thread at will; you aren’t limited to starting threads from only the main thread. The thread::create command returns immediately, and its return value is the ID of the thread created. The ID is a unique token that you use to interact with and manipulate the thread, in much the same way as you use a channel identifier returned by open to interact with and manipulate that channel. There are several commands available for introspection on thread IDs: thread::id returns the ID of the current thread; thread::names returns a list of threads currently in existence; and thread::exists tests for the existence of a given thread.The thread::create command accepts a Tcl script as an argument. If you provide a script, the interpreter in the newly created thread executes it and then terminates the thread. Example 21–1 demonstrates this by creating a thread to perform a recursive search for files in a directory. For a large directory structure,Getting Started with the Thread Extension325 this could take considerable time. By performing the search in a separate thread, the main thread is free to perform other operations in parallel. Also note how the “worker” thread loads an extension and opens a file, completely independent of any extensions loaded or files opened in other threads.Example 21–1Creating a separate thread to perform a lengthy operation.package require Thread# Create a separate thread to search the current directory# and all its subdirectories, recursively, for all files# ending in the extension ".tcl". Store the results in the# file "files.txt".thread::create {# Load the Tcllib fileutil package to use its# findByPattern procedure.package require fileutilset files [fileutil::findByPattern [pwd] *.tcl]set fid [open files.txt w]puts $fid [join $files \n]close $fid}# The main thread can perform other tasks in parallel...If you don’t provide a script argument to thread::create, the thread’s interpreter enters its event loop. You then can use the thread::send command, described on page 328, to send it scripts to evaluate. Often though, you’d like to perform some initialization of the thread before having it enter its event loop. To do so, use the thread::wait command to explicitly enter the event loop after per-forming any desired initialization, as shown in Example 21–2. You should always use thread::wait to cause a thread to enter its event loop, rather than vwait or tkwait, for reasons discussed in “Preserving and Releasing Threads” on page 330.Example 21–2Initializing a thread before entering its event loop.set httpThread [thread::create {package require httpthread::wait}]After creating a thread, never assume that it has started executing.There is a distinction between creating a thread and starting execution of a thread. When you create a thread, the operating system allocates resources for326 Multi-Threaded Tcl Scripts Chap. 21 the thread and prepares it to run. But after creation, the thread might not start execution immediately. It all depends on when the operating system allocates execution time to the thread. Be aware that the thread::create command returns when the thread is created, not necessarily when it has started. If your application has any inter-thread timing dependencies, always use one of the thread synchronization techniques discussed in this chapter.Creating Joinable ThreadsRemember that the main thread must be the last to terminate. Therefore you often need some mechanism for determining when it’s safe for the main thread to exit. Example 21–3 shows one possible approach: periodically checking thread::names to see if the main thread is the only remaining thread.Example 21–3Creating several threads in an application.package require Threadputs "*** I'm thread [thread::id]"# Create 3 threadsfor {set thread 1} {$thread <= 3} {incr thread} {set id [thread::create {# Print a hello message 3 times, waiting# a random amount of time between messagesfor {set i 1} {$i <= 3} {incr i} {after [expr { int(500*rand()) }]puts "Thread [thread::id] says hello"}}] ;# thread::createputs "*** Started thread $id"} ;# forputs "*** Existing threads: [thread::names]"# Wait until all other threads are finishedwhile {[llength [thread::names]] > 1} {after 500}puts "*** That's all, folks!"A better approach in this situation is to use joinable threads, which are supported in Tcl 8.4 or later. A joinable thread allows another thread to wait upon its termination with the thread::join command. You can useGetting Started with the Thread Extension327 thread::join only with joinable threads, which are created by including the thread::create-joinable option. Attempting to join a thread not created with -joinable results in an error. Failing to join a joinable thread causes memoryand other resource leaks in your application. Example 21–4 revises the program from Example 21–3 to use joinable threads.Example 21–4Using joinable threads to detect thread termination.package require Threadputs "*** I'm thread [thread::id]"# Create 3 threadsfor {set thread 1} {$thread <= 3} {incr thread} {set id [thread::create -joinable {# Print a hello message 3 times, waiting# a random amount of time between messagesfor {set i 1} {$i <= 3} {incr i} {after [expr { int(500*rand()) }]puts "Thread [thread::id] says hello"}}] ;# thread::createputs "*** Started thread $id"lappend threadIds $id} ;# forputs "*** Existing threads: [thread::names]"# Wait until all other threads are finishedforeach id $threadIds {thread::join $id}puts "*** That's all, folks!"The thread::join command blocks.Be aware that thread::join blocks. While the thread is waiting for thread::join to return, it can’t perform any other operations, including servic-ing its event loop. Therefore, make sure that you don’t use thread::join in situ-ations where a thread must be responsive to incoming events.328 Multi-Threaded Tcl Scripts Chap. 21 Sending Messages to ThreadsThe thread::send command sends a script to another thread to execute. The target thread’s main interpreter receives the script as a special type of event added to the end of its event queue. A thread evaluates its messages in the order received along with all other types of events. Obviously, a thread must be in its event loop for it to detect and respond to messages. As discussed on page 324, a thread enters its event loop if you don’t provide a script argument to thread::create, or if you include the thread::wait command in the thread’s initialization script.Synchronous Message SendingBy default, thread::send blocks until the target thread finishes executing the script. The return value of thread::send is the return value of the last com-mand executed in the script. If an error occurs while evaluating the script, the error condition is “reflected” into the sending thread; thread::send generates the same error code, and the target thread’s stack trace is included in the value of the errorInfo variable of the sending thread:Example 21–5Examples of synchronous message sending.set t [thread::create];# Create a thread=> 1572set myX 42;# Create a variable in the main thread=> 42# Copy the value to a variable in the worker threadthread::send $t [list set yourX $myX]=> 42# Perform a calculation in the worker threadthread::send $t {expr { $yourX / 2 } }=> 21thread::send $t {expr { $yourX / 0 } }=> divide by zerocatch {thread::send $t {expr { $yourX / 0 } } } ret=> 1puts $ret=> divide by zeroputs $errorInfo=> divide by zerowhile executing"expr { $yourX / 0 } "invoked from within"thread::send $t {expr { $yourX / 0 } } "If you also provide the name of a variable to a synchronous thread::send, then it behaves analogously to a catch command; thread::send returns the return code of the script, and the return value of the last command executed inSending Messages to Threads329 the script — or the error message — is stored in the variable. Tcl stores the tar-get thread’s stack trace in the sending thread’s errorInfo variable.Example 21–6Using a return variable with synchronous message sending.thread::send $t {incr yourX 2} myY=> 0puts $myY=> 44thread::send $t {expr { acos($yourX) } } ret=> 1puts $ret=> domain error: argument not in valid rangeputs $errorInfo=> domain error: argument not in valid rangewhile executing"expr { acos($yourX) } "While the sending thread is waiting for a synchronous thread::send to return, it can’t perform any other operations, including servicing its event loop. Therefore, synchronous sending is appropriate only in cases where:•you want a simple way of getting a value back from another thread;•you don’t mind blocking your thread if the other thread takes a while to respond; or•you need a response from the other thread before proceeding.Watch out for deadlock conditions with synchronous message sending.If Thread A performs a synchronous thread::send to Thread B, and while evaluating the script Thread B performs a synchronous thread::send to Thread A, then your application is deadlocked. Because Thread A is blocked in its thread::send, it is not servicing its event loop, and so can’t detect Thread B’s message.This situation arises most often when the script you send calls procedures in the target thread, and those procedures contain thread::send commands. Under these circumstances, it might not be obvious that the script sent will trig-ger a deadlock condition. For this reason, you should be cautious about using synchronous thread::send commands for complex actions. Sending in asynchro-nous mode, described in the next section, avoids potential deadlock situations like this.Asynchronous Message SendingWith the -async option, thread::send sends the script to the target thread in asynchronous mode. In this case, thread::send returns immediately.By default, an asynchronous thread::send discards any return value of the script. However, if you provide the name of a variable as an additional argument to thread::send, the return value of the last command executed in the script is330 Multi-Threaded Tcl Scripts Chap. 21 stored as the value of the variable. You can then either vwait on the variable or create a write trace on the variable to detect when the target thread responds. For example:thread::send -async $t [list ProcessValues $vals] resultvwait resultIn this example, the thread::send command returns immediately; the sending thread could then continue with any other operations it needed to per-form. In this case, it executes a vwait on the return variable to wait until the tar-get thread finishes executing the script. However, while waiting for the response, it can detect and process incoming events. In contrast, the following synchronous thread::send blocks, preventing the sending thread from processing events until it receives a response from the target thread:thread::send $t [list ProcessValues $vals] result Preserving and Releasing ThreadsA thread created with a script not containing a thread::wait command termi-nates as soon as the script finishes executing. But if a thread enters its event loop, it continues to run until its event loop terminates. So how do you terminate a thread’s event loop?Each thread maintains an internal reference count. The reference count is set initially to 0, or to 1 if you create the thread with the thread::create -pre-served option. Any thread can increment the reference count afterwards by exe-cuting thread::preserve, and decrement the reference count by executing thread::release. These commands affect the reference count of the current thread unless you specify the ID of another thread. If a call to thread::release results in a reference count of 0 or less, the thread is marked for termination.The use of thread reference counts allows multiple threads to preserve the existence of a worker thread until all of the threads release the worker thread. But the majority of multi-threaded Tcl applications don’t require that degree of thread management. In most cases, you can simply create a thread and then later use thread::release to terminate it:set worker [thread::create]thread::send -async $worker $script# Later in the program, terminate the worker threadthread::release $workerA thread marked for termination accepts no further messages and discards any pending events. It finishes processing any message it might be executing currently, then exits its event loop. If the thread entered its event loop through a call to thread::wait, any other commands following thread::wait are executed before thread termination, as shown in Example 21–7. This can be useful for per-forming “clean up” tasks before terminating a thread.Error Handling331 Example 21–7Executing commands after thread::wait returns.set t [thread::create {puts "Starting worker thread"thread::wait# This is executed after the thread is releasedputs "Exiting worker thread"}]Note that if a thread is executing a message script when thread::release is called (either by itself or another thread), the thread finishes executing its message script before terminating. So, if a thread is stuck in an endless loop, calling thread::release has no effect on the thread. In fact, there is no way to kill such a “runaway thread.”Always use thread::wait to enter a thread’s event loop.This system for preserving and releasing threads works only if you use the thread::wait command to enter the thread’s event loop (or if you did not provide a creation script when creating the thread). If you use vwait or tkwait to enter the event loop, thread::release cannot terminate the thread.Error HandlingIf an error occurs while a thread is executing its creation script (provided by thread::create), the thread dies. In contrast, if an error occurs while processing a message script (provided by thread::send), the default behavior is for the thread to stop execution of the message script, but to return to its event loop and continue running. To cause a thread to die when it encounters an uncaught error, use the thread::configure command to set the thread’s -unwindonerror option to true:thread::configure $t -unwindonerror 1Error handling is determined by the thread creating the thread or sending the message. If an error occurs in a script sent by a synchronous thread::send, then the error condition is “reflected” to the sending thread, as described in “Syn-chronous Message Sending” on page 328. If an error occurs during thread cre-ation or an asynchronous thread::send, the default behavior is for Tcl to send a stack trace to the standard error channel. Alternatively, you can specify the name of your own custom error handling procedure with thread::errorproc. Tcl automatically calls your procedure whenever an “asynchronous” error occurs, passing it two arguments: the ID of the thread generating the error, and the stack trace. (This is similar to defining your own bgerror procedure, as described in “The bgerror Command” on page 202.) For example, the following code logs all uncaught errors to the file errors.txt:332 Multi-Threaded Tcl Scripts Chap. 21 Example 21–8Creating a custom thread error handler.set errorFile [open errors.txt a]proc logError {id error} {global errorFileputs $errorFile "Error in thread $id"puts $errorFile $errorputs $errorFile ""}thread::errorproc logErrorShared ResourcesThe present working directory is a resource shared by all interpreters in all threads. If one thread changes the present working directory, then that change affects all interpreters and all threads. This can pose a significant problem, as some library routines temporarily change the present working directory during execution, and then restore it before returning. But in a multi-threaded applica-tion, another thread could attempt to access the present working directory dur-ing this period and get incorrect results. Therefore, the safest approach if your application needs to access the present working directory is to store this value in a global or thread-shared variable before creating any other threads. The follow-ing example uses tsv::set to store the current directory in the pwd element of the application shared variable:package require Thread# Save the pwd in a thread-shared variabletsv::set application pwd [pwd]set t [thread::create {#...}]Environment variables are another shared resource. If one thread makes a change to an environment variable, then that change affects all threads in your application. This might make it tempting to use the global env array as a method for sharing information between threads. However, you should not do so, because it is far less efficient than thread-shared variables, and there are subtle differ-ences in the way environment variables are handled on different platforms. If you need to share information between threads, you should instead use thread-shared variables, as discussed in “Shared Variables” on page 337.The exit command kills the entire application.Although technically not a shared resource, it’s important to recognize that the exit command kills the entire application, no matter which thread executes it. Therefore, you should never call exit from a thread when your intention is to terminate only that thread.Managing I/O Channels333 Managing I/O ChannelsChannels are shared resources in most programming languages. But in Tcl, channels are implemented as a per-interpreter resource. Only the standard I/O channels (stdin, stdout, and stderr) are shared.Be careful with standard I/O channel on Windows and Macintosh.When running wish on Windows and Macintosh prior to OS X, you don’t have real standard I/O channels, but simulated stdout and stderr channels direct output to the special console window. As of Thread 2.5, these simulated channels appear in the main thread’s channel list, but not in any other thread’s channel list. Therefore, you’ll cause an error if you attempt to access these chan-nels from any thread other than the main thread.Accessing Files from Multiple ThreadsIn a multi-threaded application, avoid having the same file open in multiple threads. Having the same file open for read access in multiple threads is safe, but it is more efficient to have only one thread read the file and then share the information with other threads as needed. Opening the same file in multiple threads for write or append access is likely to fail. Operating systems typically buffer information written to a disk on a per-channel basis. With multiple chan-nels open to the same file, it’s likely that one thread will end up overwriting data written by another thread. If you need multiple threads to have write access to a single file, it’s far safer to have one thread responsible for all file access, and let other threads send messages to the thread to write the data. Example 21–9 shows the skeleton implementation of a logging thread. Once the log file is open, other threads can call the logger’s AddLog procedure to write to the log file.Example 21–9A basic implementation of a logging thread.set logger [thread::create {proc OpenLog {file} {global fidset fid [open $file a]}proc CloseLog {} {global fidclose $fid}proc AddLog {msg} {global fidputs $fid $msg}thread::wait}]。