Skip to content

windows

C# 多线程操作整理 (System.Threading)

csharplogo

Thread 类

创建并控制线程,设置其优先级并获取其状态。

命名空间:System.Threading 程序集:mscorlib(在 mscorlib.dll 中)

概述与概念

C#支持通过多线程并行地执行代码,一个线程有它独立的执行路径,能够与其它的线程同时地运行。一个C#程序开始于一个单线程,这个单线程是被CLR和操作系统(也称为“主线程”)自动创建的,并具有多线程创建额外的线程。

1.使用线程的情况

  • ①.程序需要执行和两个和多个任务
  • ②.程序要等待某事件的发生:例如用户输入、文件操作、网络操作和搜索
  • ③.后台程序

2.多线程的并发执行 如果有多个线程在执行,单CPU只有一个,到底执行的哪个?

  • ①.如果一个线程连续占用CPU资源时间过长,其它的资源得不到执行,      则系统会强制的切换执行其它线程。(强制剥夺)
  • ②.如果一个线程没事可做、CPU可执行其它线程。(主动放弃)
  • ③.这是由操作系统的调度机制决定的,不同的操作系统调度机制不一样。    一般无法精确的预料多线程的执行顺序,在程序设计的时候应特别注意

3.创建并启动线程

ThreadStart 线程启动委托名=new ThreadStart(方法名);

Thread 线程实例名=new Thread(线程启动委托名); 线程实例名.Start();

4.终止线程

  • ①.线程实例名.Abort();用此方法的后果是不可恢复的终止线程。
  • ②.线程实例名.Interrupt();中断后可恢复

5.休眠线程

  • ①.线程实例名.Sleep();     当线程Sleep时,系统就立即退出执行队列一段时间,当睡眠结束时,系统会产生一个时钟中断,从而     使线程回到执行队列中,从而恢复线程的执行。

6.挂起/恢复线程

  • ①.线程实例名.Suspend();挂起     与线程休眠不同,线程的挂起不会使线程立即停止执行,直到线程到达安全点之后它才可以将该线程挂起,如果线程尚未启动或已经停止,则它将不能挂起。
  • ②.线程实例名.Resume();恢复      将使一个线程跳出挂起状态并使该线程继续执行。     一个线程不能对另一个线程调用Sleep() ,但是一个线程可以对另一个线程调用Suspend()。     还可以使用许多其它的方式来阻塞线程。例如,可以通过调用 Thread.Join 使一个线程等待另一个线程 (子线程)停止。使用Monitor.Wait使一个线程等待访问一个同步对象。

7.串行化线程

  • ①.线程实例名.jion();     例如在主线程中插入t.jion();      主线程执行到这条语句后,主线程(当前线程)立即进入阻塞状态.直到t运行完后阻塞状态才解除。相当于把t的任务插入或串联到主线程中,把两条线索串联成一条线索

8.线程的锁定机制 线程的锁定机制可以保证每次只有一个线程可以访问共享资源。 使用关键字lock

  • ①.lock语句的语法      lock(对象引用)语句块;
  • ②.lock语句的功能      当对象被lock 锁定时,访问该线程的其它线程会进入等待的状态。
  • ③.对象锁机制保证了对象访问的完整性:只有一个线程完成操作后,其它的线程才能进行操作。
  • ④.一般情况下,当一个线程写某个变量,而同时可能有其它的线程读或写这个变量时,为了保持数据的一 致性应该使用锁定机制。
  • ⑤.线程的安全性      线程安全性就是保护的类的成员和代码的安全,从而使他们不会同时被几个线程中断,使用锁定机制。
  • ⑥.多线程公用一个对象时,就不应该使用lock关键字了,这里Monitor,Monitor提供了使线程共享资源的方 案。 Monitor类可以锁定一个对象,一个线程只有得到这把锁才可以对该对象进行操作。 如: Monitor.Enter(obj);
    Monitor.Exit(obj);
  • ⑦.临界区和锁 当谈论多线程应用程序的时候,首先应该想到的就是并发性问题。尽管这对于同时执行多个任务的程序是很有用的,但通常都是危险的。为了解决这个问题,在C#中提出了临界区和锁的概念。在程序设计中,临界区是一块在任何时候只能有一个进程进入的区域。在C#中通过语句lock来声明临界区。lock声明后面的代码,不管是以行还是一块代码,在同一时间最多只能有一个进程执行。

9.线程的优先级具有不可靠性,就是说不能用优先级来控制线程的执行顺序。

10.后台线程

  • ①.什么是后台线程?比起应用程序的主图形用户界面(GUI)线程来说,这些线程以较低的优先权在不同的过程中运行着。对于不能立即执行结束, 又不想一直等待的任务,后台线程能很好的胜任。在C#中,把线程对象的  IsBackground属性设为true,该线程即为后台线程。    后台线程跟前台线程只有一个区别,那就是后台线程不妨碍程序的终止。一旦一个进程所有的前台线程都终止后,CLR将通过调用任意一个存活中的后台进程的Abort()方法来彻底终止进程。注意:后台线程不能直接操作所在进程之外的数据引用。
  • ②.怎样与后台线程通讯?运用MethodInvoker委派实体。也可在初始化(构造函数)中加入下面一句即可实现通讯:

Control.CheckForIllegalCrossThreadCalls = False;

要使用MethodInvoker委派,需要三个条件:

  •   a.一个创建委派的后台线程
Thread thread=new Thread(new ThreadStart(Run));

thread.IsBackground=true;  //把Thread设为后台线程

thread.Start();
  •   b.一个用作后台线程与前台可视化单元的接口的类级方法
 public void Run()
        {
            int count = 0;
            try
            {
                MethodInvoker mi = new MethodInvoker(this.UpdateLabel);
                //创建一个委托,UpdateLabel是该委托所托管的代码,必须是声明为void 且不接受任何参数的任何方法。
                while (true)
                {
                    count++;
                    //this.Invoke(mi);//同步执行委托
                    this.BeginInvoke(mi);//异步执行委托
                    Thread.Sleep(500);
                }
            }
            catch (ThreadInterruptedException e)
            {
                Console.WriteLine("Interruption Exception in Thread:{0}", e);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception in Thread:{0}", ex);
            }
        }
  •   c.一个应用程序中可以更新的可视化单元
public void UpdateLabel()
{     
    label1.Text=count.ToString();   
}

 

 

如何判断一个应用程序是 控制台(console)程序还是 GUI 程序

2012年2月6日作者:enet

 

Article ID: 90493 – Last Review: November 21, 2006 – Revision: 3.2

HOWTO: How To Determine Whether an Application is Console or GUI

This article was previously published under Q90493
————————————————————–

若要确定应用程序是否为控制台或 GUI,您必须分析该 EXEheader。标头包含一个称为子系统的字段。此字段确定是要在其下运行的应用程序子系统和它需要的接口的类型。值组成:

IMAGE_SUBSYSTEM_NATIVE               1
IMAGE_SUBSYSTEM_WINDOWS_GUI          2
IMAGE_SUBSYSTEM_WINDOWS_CUI          3
IMAGE_SUBSYSTEM_OS2_CUI              5
IMAGE_SUBSYSTEM_POSIX_CUI            7
IMAGE_SUBSYSTEM_NATIVE_WINDOWS       8
IMAGE_SUBSYSTEM_WINDOWS_CE_GUI       9

示例代码

#include <windows.h>
#include <winnt.h>

VOID  main(int, char **);
DWORD AbsoluteSeek(HANDLE, DWORD);
VOID  ReadBytes(HANDLE, LPVOID, DWORD);
VOID  WriteBytes(HANDLE, LPVOID, DWORD);
VOID  CopySection(HANDLE, HANDLE, DWORD);

#define XFER_BUFFER_SIZE 2048

VOID
main(int argc, char *argv[])
{
HANDLE hImage;

DWORD  bytes;
DWORD  iSection;
DWORD  SectionOffset;
DWORD  CoffHeaderOffset;
DWORD  MoreDosHeader[16];

ULONG  ntSignature;

IMAGE_DOS_HEADER      image_dos_header;
IMAGE_FILE_HEADER     image_file_header;
IMAGE_OPTIONAL_HEADER image_optional_header;
IMAGE_SECTION_HEADER  image_section_header;

if (argc != 2)
{
printf("USAGE: %s program_file_name\n", argv[1]);
exit(1);
}

/*
*  Open the reference file.
*/
hImage = CreateFile(argv[1],
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);

if (INVALID_HANDLE_VALUE == hImage)
{
printf("Could not open %s, error %lu\n", argv[1], GetLastError());
exit(1);
}

/*
*  Read the MS-DOS image header.
*/
ReadBytes(hImage,
&image_dos_header,
sizeof(IMAGE_DOS_HEADER));

if (IMAGE_DOS_SIGNATURE != image_dos_header.e_magic)
{
printf("Sorry, I do not understand this file.\n");
exit(1);
}

/*
*  Read more MS-DOS header.       */
ReadBytes(hImage,
MoreDosHeader,
sizeof(MoreDosHeader));

/*
*  Get actual COFF header.
*/
CoffHeaderOffset = AbsoluteSeek(hImage, image_dos_header.e_lfanew) +
sizeof(ULONG);

ReadBytes (hImage, &ntSignature, sizeof(ULONG));

if (IMAGE_NT_SIGNATURE != ntSignature)
{
printf("Missing NT signature. Unknown file type.\n");
exit(1);
}

SectionOffset = CoffHeaderOffset + IMAGE_SIZEOF_FILE_HEADER +
IMAGE_SIZEOF_NT_OPTIONAL_HEADER;

ReadBytes(hImage,
&image_file_header,
IMAGE_SIZEOF_FILE_HEADER);

/*
*  Read optional header.
*/
ReadBytes(hImage,
&image_optional_header,
IMAGE_SIZEOF_NT_OPTIONAL_HEADER);

switch (image_optional_header.Subsystem)
{
case IMAGE_SUBSYSTEM_UNKNOWN:
printf("Type is unknown.\n");
break;

case IMAGE_SUBSYSTEM_NATIVE:
printf("Type is native.\n");
break;

case IMAGE_SUBSYSTEM_WINDOWS_GUI:
printf("Type is Windows GUI.\n");
break;

case IMAGE_SUBSYSTEM_WINDOWS_CUI:
printf("Type is Windows CUI.\n");
break;

case IMAGE_SUBSYSTEM_OS2_CUI:
printf("Type is OS/2 CUI.\n");
break;

case IMAGE_SUBSYSTEM_POSIX_CUI:
printf("Type is POSIX CUI.\n");
break;

case IMAGE_SUBSYSTEM_NATIVE_WINDOWS:
printf("Type is native Win9x driver.\n");
break;

case IMAGE_SUBSYSTEM_WINDOWS_CE_GUI:
printf("Type is Windows CE.\n");
break;

default:
printf("Unknown type %u.\n", image_optional_header.Subsystem);
break;
}
}

DWORD
AbsoluteSeek(HANDLE hFile,
DWORD  offset)
{
DWORD newOffset;

if ((newOffset = SetFilePointer(hFile,
offset,
NULL,
FILE_BEGIN)) == 0xFFFFFFFF)
{
printf("SetFilePointer failed, error %lu.\n", GetLastError());
exit(1);
}

return newOffset;
}

VOID
ReadBytes(HANDLE hFile,
LPVOID buffer,
DWORD  size)
{
DWORD bytes;

if (!ReadFile(hFile,
buffer,
size,
&bytes,
NULL))
{
printf("ReadFile failed, error %lu.\n", GetLastError());
exit(1);
}
else if (size != bytes)
{
printf("Read the wrong number of bytes, expected %lu, got %lu.\n",
size, bytes);
exit(1);
}
}

 

添加,删除右键MS-DOS菜单设置

2012年2月6日作者:enet

 

@echo off
color 3f
title 右键MS-DOS设置
:menu
cls
echo.
echo [1] 添加右键打开MS-DOS
echo [2] 删除右键打开MS-DOS
echo [0] 退出

set /p choice= 请选择(1;2;0):

if “%choice%”==”1″ goto add
if “%choice%”==”2″ goto remove
if “%choice%”==”0″ goto end

goto menu

:add
reg add “HKCR\*\shell\MS-DOS” /ve /d MS-DOS /f >nul 2>nul
reg add “HKCR\*\shell\MS-DOS\command” /ve /d “cmd.exe” /f >nul 2>nul
reg add “HKCR\Folder\shell\MS-DOS” /ve /d MS-DOS /f >nul 2>nul
reg add “HKCR\Folder\shell\MS-DOS\command” /ve /d “cmd.exe /k cd %%1″ /f >nul 2>nul
cls
echo 操作成功,按任意键返回……
pause>nul
goto menu

:remove
reg delete “HKCR\*\shell\MS-DOS” /f >nul 2>nul
reg delete “HKCR\Folder\shell\MS-DOS” /f >nul 2>nul
cls
echo 操作成功,按任意键返回……
pause>nul
goto menu

:end
cls
echo —————————————————–
echo 操作完毕,谢谢您的使用!
echo.
echo.
echo 按任意键退出…
echo —————————————————–
pause>nul

 

拷贝到文本文件。 后缀名为bat,执行即可。

 

Cheat Engine 6.1 的中文语言文件

2011年11月10日作者:enet

 

看到 Cheat Engine 6.1 作者为CE添加了多语言文件. 下载了 zh_CN.zip,  解压把 zh_CN 文件夹直接放到了 languages下. 打开发现依然是E文的.

接着尝试直接把 cheatengine-x86_64.po 和 lclstrconsts.po 放到  languages 下, 覆盖原文件.  再打开CE仍然是E文的.

最后发现在 languages 目录下 创建 Ch_Ch 文件夹,把文件 cheatengine-x86_64.po 和  lclstrconsts.po 复制到 Ch_Ch 目录下即可。

所以应该直接解压后, 拷贝 zh_CN 文件夹放到了 languages 下, 然后改成 Ch_Ch 就行了.

 

P.S. 还有就是CE的文件夹路径里, 不要有中文路径.

Windows 7 远程桌面,提示“凭据不工作”

2011年10月29日作者:enet

 

我的两台机器都是Windows 7 旗舰版 , 最近发现一个问题。

A PC登录另一台 B PC的远程登录, 总是提示“凭据不工作”。但是密码帐号肯定正确。 但B登录A却没有问题。

 

开始很疑惑, 修改了很久设置,结果还是一样

后来在微软问答: http://answers.microsoft.com/ 找到了答案

就是要换成 控制面板–>用户账户  中的那个用户名, 就可以了

http://answers.microsoft.com/zh-hans/windows/forum/windows_7-security/win-7/61147f4b-367f-4d29-b350-8c84d09dc542?page=1

win7下的两种方法:

 

1.  可以查看 控制面板-用户账户,原因是用户名称和显示的名称不是一样的。(比如显示的Administrator可登录用户名却是Mr-Lee)

打开“配置高级用户配置文件属性”可以看到

 

2.  查看要登录的win7系统的 C:\Users 文件夹,使用文件夹中的对应的那个用户名称.。原因同上。

可以查看 控制面板-用户账户,原来是两台机器的用户名称和显示的Administrator不是一样的。

打开“配置高级用户配置文件属性”可以看到

最简单的方法就是直接打开系统盘下的 “用户”文件夹查看

C:\Users\Mr.Tang

C:\Users\Administrator

http://answers.microsoft.com/zh-hans/windows/forum/windows_7-security/win-7/61147f4b-367f-4d29-b350-8c84d09dc542?page=1